首页 > 编程语言 >android IO Prefetch源码分析

android IO Prefetch源码分析

时间:2024-08-01 17:18:03浏览次数:17  
标签:name list bindApp 源码 Prefetch ux android tracker pkg

I/O Prefetcher是高通本身提供的一套优化方案,可以用在Android手机App冷启动的时候。本文基于android Q

主要分libqti-iopd、vendor.qti.hardware.iop@2.0-impl、libqti-iopd-client_system、libqti-perfd-client_system、libperfconfig、libqti_performance,编译后在/vendor/lib/目录下,其中libqti-iopd、vendor.qti.hardware.iop@2.0-impl为服务端

  • libqti-perfd-client_system

主要提供perf_get_prop、perf_wait_get_prop等方法,这里主要调用libperfconfig中的方法去读取配置文件中的信息

代码路径:vendor/qcom/proprietary/commonsys-intf/android-perf/mp-ctl/client.cpp

  • libperfconfig

主要是读取/vendor/etc/perf/perfconfigstore.xml文件获取配置信息,此文件时在编译时拷贝

vendor/qcom/proprietary/android-perf/configs/XXX/perfconfigstore.xml到vendor/etc/perf/目录下

代码路径:vendor/qcom/proprietary/android-perf/perf-hal/Perf.h

  • libqti-iopd-client_system

主要提供perf_io_prefetch_start、perf_io_prefetch_stop、perf_ux_engine_events、perf_ux_engine_trigger方法,这些方法主要调用vendor.qti.hardware.iop@2.0-impl中的iopStart、iopStop、uxEngine_events、uxEngine_trigger

代码路径: vendor/qcom/proprietary/commonsys-intf/android-perf/io-p/client.cpp

  • libqti_performance

vendor/qcom/proprietary/commonsys/android-perf/QPerformance/src/com/qualcomm/qti/Performance.java的jni实现

  • vendor.qti.hardware.iop@2.0-impl

主要提供iopStart、iopStop和uxEngine_events、uxEngine_trigger方法,是对libqti-iopd的方法的封装uxEngine_XX会判断perfconfigstore.xml中vendor.iop.enable_uxe属性是否为1来判断是否启用,如果未启用则直接返回代码路径: vendor/qcom/proprietary/android-perf/iop-hal/Iop.cpp

  • libqti-iopd

主要服务端实现库,见下文分析

代码路径:vendor/qcom/proprietary/android-perf/io-p/io-p.cpp

首先看服务端的实现

IO Prefetcher的初始化

vendor.qti.hardware.iop的实现比较简单,主要调用libqti-iopd中对应方法。在初始化时,打开libqti-iopd.so库,然后找到iop_server_init、iop_server_exit、iop_server_submit_request、uxe_server_submit_request四个方法,并调用iop_server_init进行初始化。

对应关系如下:

  • 在调用iopStart、iopStop和uxEngine_events时,调用libqti-iopd中iop_server_submit_request,并传递cmd分别为IOP_CMD_PERFLOCK_IOPREFETCH_START、IOP_CMD_PERFLOCK_IOPREFETCH_STOP和UXENGINE_CMD_PERFLOCK_EVENTS的消息,并放入IOPevqueue队列中

  • 在调用uxEngine_trigger时,调用libqti-iopd中uxe_server_submit_request,并传递cmd为UXENGINE_CMD_PERFLOCK_TRIGGER的消息,并放入UXEevqueue队列中

* vendor/qcom/proprietary/android-perf/iop-hal/Iop.cpp
IIop* HIDL_FETCH_IIop(const char* /* name */) {
    ALOGE("IOP-HAL: inside HIDL_FETCH_IIop");
    Iop *iopObj = new (std::nothrow) Iop();
    ALOGE("IOP-HAL: boot Address of iop object");
    if (iopObj != NULL) {
        iopObj->LoadIopLib();
        ALOGE("IOP-HAL: loading library is done");
        if (iopObj->mHandle.iop_server_init != NULL ) {
            (*(iopObj->mHandle.iop_server_init))();
        }
    }
    return iopObj;
}

//加载libqti-iopd库,并找到对应的方法
void Iop::LoadIopLib() {
    const char *rc = NULL;
    char buf[PROPERTY_VALUE_MAX];

    if (!mHandle.is_opened) {
         mHandle.dlhandle = dlopen("libqti-iopd.so", RTLD_NOW | RTLD_LOCAL);
         ...
         *(void **) (&mHandle.iop_server_init) = dlsym(mHandle.dlhandle, "iop_server_init");
         ...
         *(void **) (&mHandle.iop_server_exit) = dlsym(mHandle.dlhandle, "iop_server_exit");
         ...
         *(void **) (&mHandle.iop_server_submit_request) = dlsym(mHandle.dlhandle, "iop_server_submit_request");
         ...
         *(void **) (&mHandle.uxe_server_submit_request) = dlsym(mHandle.dlhandle, "uxe_server_submit_request");
         ...
         mHandle.is_opened = true;
    }

    return;
}

真正的初始化:

* vendor/qcom/proprietary/android-perf/io-p/io-p.cpp
int iop_server_init() {
    ...
    //创建IOP服务,进入无限循环来等待消息
    rc1 = pthread_create(&iop_server_thread, NULL, iop_server, NULL);
    ...
    //读取vendor.iop.enable_uxe是否为1来判断是否启用,1则启用
    if (uxe_disabled()) {
        uxe_prop_disable = 1;
    } else {
        uxe_prop_disable = 0;
        ...
        //创建UXE服务,进入无限循环来等待消息
        rc2 = pthread_create(&uxe_server_thread, NULL, uxe_server, NULL);
        uba_rc = init_uba();
        ...
        uxe_init = true;
    }

    return 1;
...
}

IOP服务消息处理,从IOPevqueue消息队列中获取消息并处理:

* vendor/qcom/proprietary/android-perf/io-p/io-p.cpp
static void *iop_server(void *data)
{
    ...
    /* Main loop */
    for (;;) {
       //wait for perflock commands
        EventData *evData = IOPevqueue.Wait();
        ...
        //判断vendor.post_boot.parsed表示是否为1来判断系统是否启动完成
        if(!is_boot_complete())
        {
            QLOGE("io prefetch is disabled waiting for boot_completed");
            continue;
        }
        //创建DB
        if(is_db_init == false)
        {
            if(create_database() == 0)
            {
                //Success
                is_db_init = true;
            }
        }
        ...
        switch (cmd) {
            //uxEngine_events方法实现
            case UXENGINE_CMD_PERFLOCK_EVENTS:
            {
                ...
                break;
            }
            //iopStart方法实现
            case IOP_CMD_PERFLOCK_IOPREFETCH_START:
            {
                ...
                break;
            }
            //iopStop方法实现
            case IOP_CMD_PERFLOCK_IOPREFETCH_STOP:
            {
                stop_capture();
                break;
            }
        }
   }
}

UXE消息处理,从UXEevqueue队列中获取一个消息,并处理

* vendor/qcom/proprietary/android-perf/io-p/io-p.cpp
static void *uxe_server(void *data)
{
    for (;;) {
        QLOGI("UXEngine: Waiting on uxe_server_submit_req in uxe_server\n");
        EventData *evData = UXEevqueue.Wait();
        if (!evData || !evData->mEvData) {
            continue;
        }
        cmd = evData->mEvType;
        msg = (iop_msg_t *)evData->mEvData;

        switch (cmd) {
            //uxEngine_trigger方法实现
            case UXENGINE_CMD_PERFLOCK_TRIGGER:
            {
                ...
                break;
            }
       }
   }
}

所以可以看出先是创建了一个database,及4个表,源码在dblayer.cppDB存储路径为/data/vendor/iop/io-prefetcher.db

* vendor/qcom/proprietary/android-perf/io-p/io-prefetch/dblayer.cpp
/******************************************************************************
  DESCRIPTION

     pkg_file_tbl               pkg_tbl
  |-----------------|      |-----------------|
  |  pkg_name       |      |  pkg_name       |
  |  file_name      |      |-----------------|
  |-----------------|      | pkg_use_time    |
  |                 |      | num_of_launches |
  | file_use_ctr    |      |-----------------|
  | file_time_stamp |
  | file_size       |
  | mark_for_delete |
  | file_modify_time|
  | file_iop_size   |
  | study_finish    |
  | mincore_array   |
  | cache_dropped   |
  | disabled        |
  |-----------------|
******************************************************************************/

/******************************************************************************
      ux_pkg_tbl                 ux_lat_tbl
  |--------------------|        |-----------------|
  |  pkg_name          |        |    pkg_name     |
  |  week_day          |        |-----------------|
  |--------------------|        |   bindApp_dur   |
  | pkg_last_use       |        |   disAct_dur    |
  | num_of_launches    |        |  wakeLock_dur   |
  | num_of_sublaunches |        |    memory_st    |
  | timeslot_1_count   |        |    bindApp_ct   |
  | timeslot_2_count   |        | predict_success |
  | timeslot_3_count   |        |   predict_fail  |
  | timeslot_4_count   |        |    not_game     |
  |--------------------|        |-----------------|
******************************************************************************/

iopStart

* vendor/qcom/proprietary/android-perf/io-p/io-p.cpp
case IOP_CMD_PERFLOCK_IOPREFETCH_START:
{
    static bool is_in_recent_list = false;
    char enable_prefetch_property[PROPERTY_VALUE_MAX];
    char enable_prefetch_ofr_property[PROPERTY_VALUE_MAX];
    int enable_prefetcher = 0;
    int enable_prefetcher_ofr = 0;
    //IOP是否启用
    strlcpy(enable_prefetch_property,perf_get_prop("vendor.enable.prefetch" , "0").value, PROPERTY_VALUE_MAX);
    enable_prefetch_property[PROPERTY_VALUE_MAX-1]='\0';

    enable_prefetcher = strtod(enable_prefetch_property, NULL);

    strlcpy(enable_prefetch_ofr_property,perf_get_prop("vendor.iop.enable_prefetch_ofr" , "0").value, PROPERTY_VALUE_MAX);
    enable_prefetch_ofr_property[PROPERTY_VALUE_MAX-1]='\0';

    enable_prefetcher_ofr = strtod(enable_prefetch_ofr_property, NULL);

    // if PID < 0 consider it as playback operation
    if(msg->pid < 0)
    {
        int ittr = 0;
        char *week_day = NULL;
        week_day = (char *) malloc(6*sizeof(char));
        // Insert package into the table
        if (week_day == NULL) {
           //Malloc failed. Most-probably low on memory.
           break;
        }
        strlcpy(pkg_info.pkg_name,msg->pkg_name,PKG_NAME_LEN);
        strlcpy(tmp_pkg_name,pkg_info.pkg_name,PKG_NAME_LEN);
        bindApp_dur = 0;
        disAct_dur = 0;
        launching = true;
        //更新DB信息
        time(&pkg_info.last_time_launched);
           //计算week_day:如果时周二~周六,则为true,否则为false
           //time_slot:4~12时,为1
           //12~17时,为2
           //17~21时,为3
           //0~4,21~24,为4
        compute_time_day_slot(week_day, &time_slot);
        QLOGI("UXEngine Updating Details: pkg_name: %s, week_day: %s, time_slot: %d %s\n", pkg_info.pkg_name, week_day, time_slot, tmp_pkg_name);
        //更新ux_pkg_tbl表信息
        update_ux_pkg_details(pkg_info, week_day, time_slot, 0);
        //更新tracker信息,当启启动应用个数达到12个时,更新ux_lat_tbl信息,并设置权重为10
        update_palm_table(msg->pkg_name, 0, 1);

        QLOGI("UXEngine finished ux_pkg_details update \n");
        free(week_day);
        is_in_recent_list = false;
        if(!enable_prefetcher)
        {
            QLOGE("io prefetch is disabled");
            break;
        }

        //检测应用是否在应用历史列表中
        for(ittr = 0; ittr < IOP_NO_RECENT_APP; ittr++)
        {
            if(0 == strcmp(msg->pkg_name,recent_list[ittr]))
            {
                is_in_recent_list = true;
                QLOGE("is_in_recent_list is TRUE");
                break;
            }
        }
        // 如果在应用历史列表中,则退出
        if(true == is_in_recent_list)
        {
            QLOGE("io prefetch is deactivate");
            break;
        }
        //假如在应用历史个数为7,则重置为0
        if(recent_list_cnt == IOP_NO_RECENT_APP)
            recent_list_cnt = 0;

        //拷贝包名到应用历史列表中
        strlcpy(recent_list[recent_list_cnt],msg->pkg_name,PKG_LEN);
        recent_list_cnt++;

        stop_capture();
        stop_playback();
        start_playback(msg->pkg_name);
    }
    // if PID > 0 then consider as capture operation
    if(msg->pid > 0)
    {
        if(!enable_prefetcher)
        {
            QLOGE("io prefetch is disabled");
            break;
        }
        if(true == is_in_recent_list)
        {
            QLOGE("io prefetch Capture is deactivated ");
            break;
        }
        stop_capture();
        //关键函数,开始进行capture抓取,启动一个线程,调用capture_thread
        start_capture(msg->pid,msg->pkg_name,msg->code_path,enable_prefetcher_ofr);
    }

    break;
}

主要在应用启动时候调用IopStart

  • 假如pid小于0,则进行playback操作
  1. 生成tmp_pkg_name(在binderApplication中甬道)和pkg_info

  2. 计算week_day:如果时周二~周六,则为true,否则为false

  3. 计算time_slot:4 ~ 12时,为1; 12 ~ 17时,为2; 17 ~ 21时,为3; 0~4或者21~24,为4

  4. 调用update_ux_pkg_details更新ux_pkg_tbl表信息,pkg_use_count和timeslot_count_字段+1

  5. 调用update_palm_table更新tracker信息,当应用kill,则cached为false,当和上次启动时间小于2s,do_not_launch为true,当应用launch,则cached为true当启启动应用个数达到12个时,更新ux_lat_tbl信息,并设置权重为10,这里的权重暂未使用

  6. 检测应用是否在应用历史列表中,如果在应用历史列表中,则退出;否则,假如应用历史个数为7,则重置为0,拷贝包名到应用历史列表中

  7. 调用start_playback,启动一个线程,调用start_playback_thread假如pid大于0,则进行start_capture操作,这里主要启动一个线程,然后执行capture_thread

从第6步开始,为IOP流程,1~5为iop和uxe共通

IOP流程关键函数为capture_thread和start_playback_thread

* vendor/qcom/proprietary/android-perf/io-p/io-prefetch/list_capture.cpp
void * capture_thread(void * arg) {
    ...
    //获取polling_enable标示是否启用,默认为启用
    property_get("vendor.polling_enable", property, "0");
    polling_enable = atoi(property);
    ATRACE_BEGIN("capture_thread");
    capture_thread_arg * arg_bundle = (capture_thread_arg *)arg;

    //获取包名:之前的字符
    pkg_len = strlen(arg_bundle->pkg_name);
    i = 0;
    while(i < pkg_len && arg_bundle->pkg_name[i] != ':')
    {
        i++;
    }
    arg_bundle->pkg_name[i] = '\0';
    strlcpy(list_pkg_name,arg_bundle->pkg_name,PKG_NAME_LEN);

    QLOGI("pkg_name  = %s",arg_bundle->pkg_name);

    if(polling_enable)
    {
        //假如开启轮询,则循环3000/50次get_snapshot,每次休眠50秒
        while (duration_counter < halt_counter) {
            if (halt_thread) goto cleanup;
            QLOGI("Getting snapshot %d\n", arg_bundle->pid);
            //获取当前pid打开的文件描述符,并放入file_list中
            get_snapshot(list_pkg_name,arg_bundle->pid);
            duration_counter++;
            usleep(read_fd_interval_ms * 1000);
        }
    }
    //获取到代码apk、oat位置,路径为/data/app/××,这里会扫描并打开文件并放入file_list中
    get_priv_code_files(arg_bundle->pkg_name);
    //获取文件位置,路径为/data/user/0/pkg和/data/data/pkg
    get_priv_files(arg_bundle);
    QLOGI("pkg_name = %s total_files = %d ",arg_bundle->pkg_name,total_files);

    // Insert package into the table
    strlcpy(pkg_info.pkg_name,arg_bundle->pkg_name,PKG_NAME_LEN);
    time(&pkg_info.last_time_launched);
    // Update Mincore data
    if(arg_bundle->ofr)
    {
        for(index = 0; index < total_files;index++)
        {
           if(update_mincore_data(file_list[index]) != 0)
           {
               file_list[index]->mincore_array = NULL;
           }
        }
    }
    //更新在database中,更新io_pkg_tbl,主要更新应用最后启动时间
    update_pkg_details(pkg_info);
    //更新io_pkg_file_tbl,将打开的文件信息存入数据库中
    update_file_details(arg_bundle->pkg_name, file_list, total_files);
    delete_mark_files();
cleanup:
    //log a report about how many files need insert or update this time
    QLOGE("# Final entry : pkg_name file_name file_time_stamp filesize file_iop_size");
    for(i = 0; i < total_files; i++) {
        QLOGE("%d. Final entry : %s %s %d %d %d\n",i
                ,arg_bundle->pkg_name,file_list[i]->file_name
                , file_list[i]->file_time_stamp, file_list[i]->filesize, file_list[i]->file_iop_size);
        if (file_list[i]->mincore_array) {
            free(file_list[i]->mincore_array);
            file_list[i]->mincore_array = NULL;
        }
        free(file_list[i]);
    }
    ATRACE_END();

    free(arg_bundle->pkg_name);
    free(arg_bundle);
    QLOGI("Exit capture_thread");
    return NULL;
}
  • 获取polling_enable标示是否启用,默认未启用,如果启用,则循环3000/50次get_snapshot,每次休眠50秒,获取/proc/pid/fd打开的文件描述符,并放入file_list中

  • 获取包名:之前的字符

  • 获取到代码apk、oat位置,路径为/data/app/××,这里会扫描并打开文件并放入file_list中

  • 获取文件位置,路径为/data/user/0/pkg和/data/data/pkg,并放入file_list中

  • 更新io_pkg_tbl,主要更新应用最后启动时间

  • 更新io_pkg_file_tbl,将打开的文件信息存入数据库中

至此,可以看到,在打开了iop之后,在打开应用的时候,会查看该pid打开的文件描述符列表,将一些需要打开的文件先缓存到数据库中。那么缓存到数据库中,该怎么使用?其实就是调用start_playback_thread,从数据库中查询到对应的文件,然后进行打开操作

start的调用流程为:

  1. 任务历史中切换应用ActivityTaskManagerService::moveTaskToFrontLocked

---ActivityStackSupervisor::findTaskToMoveToFront--------ActivityStackSupervisor::acquireAppLaunchPerfLock(当TopActivity为空或者DESTROYED)时被调用BoostFramework::perfIOPrefetchStart

  1. 应用启动RootActivityContainer::findTask----ActivityDisplay::findTaskLocked--------ActivityDisplay::acquireAppLaunchPerfLock------------BoostFramework::perfIOPrefetchStar

最终调用com.qualcomm.qti.Performance.perfIOPrefetchStart,直接调用hidl的iopStart方法及com.qualcomm.qti.UxPerformance.perfIOPrefetchStart 这里会通过MappedByteBuffer和FileChannel打开/data/app/下的文件映射到虚拟内存中

uxEngine_events

调用流程

  1. ActivityThread::handleBindApplication完成后,调用ux_perf.perfUXEngine_events(BoostFramework.UXE_EVENT_BINDAPP, 0,pkg_name,bindApp_dur,//handleBindApplication执行时间pkgDir);//data/app/pck/base.apk

  2. ActivityManagerService::appDiedLockedmUxPerf.perfUXEngine_events(BoostFramework.UXE_EVENT_KILL, 0, app.processName, 0);

  3. ProcessRecord::killux_perf.perfUXEngine_events(BoostFramework.UXE_EVENT_KILL, 0, this.processName, 0);activity显示完成,调用ActivityMetricsLogger.logAppDisplayed

  4. mUxPerf.perfUXEngine_events(BoostFramework.UXE_EVENT_DISPLAYED_ACT, 0, info.packageName, info.windowsDrawnDelayMs);

case UXENGINE_CMD_PERFLOCK_EVENTS:
 {
     if(uxe_prop_disable || !is_boot_complete())
     {
         QLOGE("UXEngine is disabled");
         break;
     }

     int opcode = msg->opcode;
     char in_pkg_name[PKG_NAME_LEN];
     strlcpy(in_pkg_name, msg->pkg_name, PKG_NAME_LEN);

     //Opcode = 2 : Client is sending bindApp duration
     if(opcode == UXE_EVENT_BINDAPP)
     {
         ...
         bindApp_dur = msg->lat;
         //获取bindApplication次数
         bindApp_count = get_total_pkg_bindapp_count(in_pkg_name);
         QLOGI("UXEngine: Received bindApp duration: %d pkg_name %s tmp_pkg_name: %s\n",
                                                 bindApp_dur, in_pkg_name, tmp_pkg_name);
         if (bindApp_count) {
             int lat_thres = 0;
             //获取ux_lat_tbl表中信息
             get_ux_lat_pkg(in_pkg_name, &ux_lat);
             QLOGI("UXEngine: Average bindApp before: %d bindApp_count : %d\n",
                                            ux_lat.bindApp_dur, bindApp_count);
             //计算lat_thres,为当前binderApplication时间和上次时间百分比
             if (ux_lat.bindApp_dur != 0)
                  lat_thres = (bindApp_dur*100)/ux_lat.bindApp_dur;
             //如果lat_thres为[50, 500]之间,且小于4s,则计算平均启动时间
             if (ux_lat.bindApp_dur != 0 && (LAT_LOW_THRESHOLD < lat_thres && LAT_HIGH_THRESHOLD > lat_thres) && bindApp_dur < 4000) {
                 avg_bindApp = ((bindApp_count) * ux_lat.bindApp_dur + bindApp_dur) / (bindApp_count+1);
             } else {
                 avg_bindApp = ux_lat.bindApp_dur;
                 if (ux_lat.bindApp_dur == 0)
                     avg_bindApp = bindApp_dur;
             }
             avg_disAct = ux_lat.disAct_dur;
             QLOGI("UXEngine: Average bindApp: %d for app: %s bindApp_count: %d\n",
                                          avg_bindApp, in_pkg_name, bindApp_count);
             bindApp_dur = avg_bindApp;
         } else {
             avg_disAct = 0;
         }
         //这种情况下为没有调用过iopStart,即为启动empty app情况下,
         //比如清除任务后,通过uxEngine_trigger获取信息后调用startActivityAsUserEmpty情况下
         if (!pkg_match)
         {
             // Empty app launch. Update db table.
             QLOGI("UXEngine: Updating bindApp duration: %d pkg_name %s\n",
                                                 bindApp_dur, in_pkg_name);
             update_ux_lat_details(in_pkg_name, bindApp_dur, avg_disAct, 0, 1);
             bindApp_dur = 0;
             avg_bindApp = 0;
             avg_disAct = 0;
         }
     }

     //Opcode = 3 : Client is sending DisplayedActivity
     if (opcode == UXE_EVENT_DISPLAYED_ACT)
     {
                int non_empty_launch = 1;
                bool pkg_match = true;
                if (tmp_pkg_name[0] == 0)
                    goto disAct_cleanup;
                //比较in_pkg_name和tmp_pkg_name(在IopStart获取)是否一致 ,launching(IopStart时赋为true)是否为true
                if (!strncmp(in_pkg_name, tmp_pkg_name, PKG_NAME_LEN)) {
                    pkg_match = true;
                } else {
                    //Received unexpected displayed activity.
                    //Update regardless, but for the correct app.
                    //skip update
                    QLOGI("UXEngine: Skip. Received weird DA: %s\n", in_pkg_name);
                    goto disAct_cleanup;
                }

                disAct_dur = msg->lat;
                //调用get_total_pkg_use_count查询ux_pkg_tbl表中pkg_use_count,即应用activity打开次数
                pkg_count = get_total_pkg_use_count(in_pkg_name);
                // Empty app launch
                //调用iopStart,但是未执行binderApplication流程,则bindApp_dur为0,launching为true
                if ((bindApp_dur == 0 && launching) || !pkg_match) {
                    QLOGI("UXEngine: Empty app launch. Just update DA. pkg_name: %s \n", in_pkg_name);
                    bindApp_count = get_total_pkg_bindapp_count(in_pkg_name);
                    get_ux_lat_pkg(in_pkg_name, &ux_lat);
                    // Launching process would have definitely had a bindApp.
                    // If count=0, something wrong. Skip update.
                    if(bindApp_count)
                        bindApp_dur = ux_lat.bindApp_dur;
                    else
                        goto disAct_cleanup;
                    avg_disAct = ux_lat.disAct_dur;
                    //空app启动流程,曾经被uxe拉起过
                    non_empty_launch = 0;
                }

                QLOGI("UXEngine: bindApp duration: %d, DisplayedActivity: %d\n", bindApp_dur, disAct_dur);
                //代表应用activity被打开过两次以上
                if (pkg_count - 1) {
                    QLOGI("UXEngine: Average displayed activity before: %d pkg_count :%d\n", avg_disAct, pkg_count);
                    int lat_thres = 0;
                    if (avg_disAct != 0)
                        lat_thres = (disAct_dur*100)/avg_disAct;
                    if (avg_disAct != 0 && pkg_count != 0 && (LAT_LOW_THRESHOLD < lat_thres && LAT_HIGH_THRESHOLD > lat_thres) && disAct_dur < 5000) {
                        avg_disAct = ((pkg_count - 1) * avg_disAct + disAct_dur) / pkg_count;
                        disAct_dur = avg_disAct;
                    }
                    QLOGI("UXEngine: Average displayed activity after: %d, bindApp_count: %d\n", avg_disAct);
                    if(avg_disAct != 0)
                        disAct_dur = avg_disAct;
                }
                QLOGI("UXEngine: Updating bindApp & DA duration: %d %d pkg_name %s\n",
                                                     bindApp_dur, disAct_dur, in_pkg_name);
                update_ux_lat_details(in_pkg_name, bindApp_dur, disAct_dur, 0, non_empty_launch);

                disAct_cleanup:
                // Refresh preferred apps & palm table after launch.
                        char *final_out[PREFERRED_APP_COUNT];
                        int uba_return = 0, i = 0;
                        for (i = 0; i < PREFERRED_APP_COUNT; i++) {
                            final_out[i] = (char*) malloc(PKG_NAME_LEN);
                            final_out[i][0] = '\0';
                        }
                        uba_return = get_preferred_apps(final_out, 0, NULL, -1, true);
                        update_palm_table(in_pkg_name, 0, 1);
                        //Cleanup
                        for (i = 0; i < PREFERRED_APP_COUNT; i++) {
                            free(final_out[i]);
                        }
                        disAct_dur = 0;
                        bindApp_dur = 0;
                        avg_bindApp = 0;
                        avg_disAct = 0;
                        pkg_count = 0;
                        launching = false;
                        memset(tmp_pkg_name, 0, PKG_NAME_LEN);
                        QLOGI("UXEngine: Finished ux_lat update & reset \n");
     }
     //应用被杀,则更新tracker及信息ux_lat_tbl信息
     if(opcode == UXE_EVENT_KILL)
     {
         QLOGI("UXEngine: Received app no-AM kill: %s\n", in_pkg_name);
         update_palm_table(in_pkg_name, 1, 0);
     }

     if(opcode == UXE_EVENT_GAME)
     {
          char *week_day = NULL;
          week_day = (char *) malloc(6*sizeof(char));
           if (week_day == NULL) {
              //Malloc failed. Most-probably low on memory.
              break;
           }
           strlcpy(pkg_info.pkg_name,in_pkg_name,PKG_NAME_LEN);
           time(&pkg_info.last_time_launched);
           compute_time_day_slot(week_day, &time_slot);
           QLOGI("UXEngine Updating sub_launch details: \
                  pkg_name: %s, week_day: %s, time_slot: %d %s\n",
                  pkg_info.pkg_name, week_day, time_slot, tmp_pkg_name);
           update_ux_pkg_details(pkg_info, week_day, time_slot, 1);
           update_palm_table(msg->pkg_name, 0, 1);
           free(week_day);
     }
     if(opcode == UXE_EVENT_SUB_LAUNCH)
     {
         ...
     }
     //应用卸载,删除db信息
     if(opcode == UXE_EVENT_PKG_UNINSTALL)
     {
         QLOGI("UXEngine: Received pkg uninstall - %s\n", in_pkg_name);
         int userId = msg->lat;
         update_palm_table(in_pkg_name, 1, 0);
         uninstall_pkg(in_pkg_name);
     }
     if(opcode == UXE_EVENT_PKG_INSTALL)
     {
         ...
     }
     break;
 }

UXE_EVENT_BINDAPP 流程,在应用bindApplication完成后调用

  1. 比较in_pkg_name和tmp_pkg_name(在IopStart获取)是否一致 ,launching(IopStart时赋为true)是否为true

  2. 调用get_total_pkg_bindapp_count查询ux_lat_tbl表中bindApp_ct获取bindApplication次数

  3. 调用get_ux_lat_pkg获取ux_lat_tbl表中信息

  4. 计算lat_thres,为当前binderApplication时间和上次时间百分比,如果lat_thres为[50, 500]之间,且小于4s,则计算平均启动时间bindApp_dur

  5. 没有调用过iopStart(即tmp_pkg_name为上次iopStart时应用包名),即为启动empty app情况下,比如清除任务后,通过uxEngine_trigger获取信息后调用startActivityAsUserEmpty情况下,则调用update_ux_lat_details更新ux_lat_tbl表中bindApp_dur(平均binderApplication时间)和disAct_dur(如果第一次为0,否则为db中存储的)信息,并使bindApp_ct+1

UXE_EVENT_DISPLAYED_ACT流程,在应用activity启动完成后,类似activity启动的displayed时间

  1. 比较in_pkg_name和tmp_pkg_name(在IopStart获取)是否一致 ,launching(IopStart时赋为true)是否为true

  2. 调用get_total_pkg_use_count查询ux_pkg_tbl表中pkg_use_count,即应用activity打开次数

  3. 调用iopStart,但是未执行binderApplication流程,则bindApp_dur为0,launching为true,即为空app启动流程,曾经被uxe拉起过。调用get_total_pkg_bindapp_count获取ux_lat_tbl中bindApp_ct,即binderApplication次数,调用get_ux_lat_pkg获取ux_lat_tbl中对应包名的信息,这里貌似有点鸡肋,get_total_pkg_bindapp_count为多余的。并之non_empty_launch为0,代表空app启动流程

  4. pkg_count - 1为真,代表应用activity被打开过两次以上,

  5. 计算lat_thres,为当前displayed activity时间和上次时间百分比,如果lat_thres为[50, 500]之间,且小于5s,则计算平均启动时间disAct_dur

  6. 调用update_ux_lat_details更新ux_lat_tbl表中bindApp_dur(平均binderApplication时间)和disAct_dur(如果第一次为0,否则为db中存储的)信息,并使bindApp_ct+1(曾经被uxe拉起过,则non_empty_launch为0,不+1)

  7. 状态重置:调用get_preferred_apps更新tracker列表调用update_palm_table设置为以启动模式(cached为true)

UXE_EVENT_KILL流程,应用被杀死后的流程

  1. 调用update_palm_table,设置tracker列表的cached和empty为false

  2. 如果kill时间距离displayed activity时间或者返回home时间间隔2s内,则do_not_launch为true,在下次返回home或者displayed时,从返回列表中移除

uxEngine_trigger

获取需要预启动的app信息

case UXENGINE_CMD_PERFLOCK_TRIGGER:
{
    static int back_to_home = 1;
    pa_ready = false;
    //判断是否启用
    if(uxe_prop_disable || !is_boot_complete())
    {
        QLOGI("UXEngine is disabled");
        pthread_cond_signal(&ux_trigger_cond);
        break;
    }
    ...
    // get_preferred_apps.
    if(msg->opcode == UXE_TRIGGER || msg->opcode == UXE_ULMK_TRIGGER)
    {
        ...
        QLOGE("UXEngine, Received back to home");
           //final_out个数为12
        char *final_out[PREFERRED_APP_COUNT];
        int uba_return = 0, i = 0;

        for (i = 0; i < PREFERRED_APP_COUNT; i++) {
            final_out[i] = (char*) malloc(PKG_NAME_LEN);
            final_out[i][0] = '\0';
        }

        if (msg->opcode == UXE_TRIGGER) {
            uba_return = get_preferred_apps(final_out, 0, NULL, -1, false);
        } else {
            uba_return = get_preferred_apps(final_out, 1, NULL, -1, false);
        }
        QLOGE("Final OUTPUT: Preferred Apps returned : %d\n", uba_return);

        for (i = 0; i < uba_return ; i++) {
            if (final_out[i][0] != 0) {
                QLOGI("Final OUTPUT: Apps returned: %s\n", final_out[i]);
                strlcat(preferred_apps,final_out[i],strcat_sz);
                strlcat(preferred_apps,"/",strcat_sz);
            }
        }
        pa_ready = true;
        // return to F/W once HIDL is implemented
        back_to_home = 1;
        for (i = 0; i < PREFERRED_APP_COUNT; i++) {
            free(final_out[i]);
        }
        QLOGE("UXEngine: Set preferred apps in uxe_server: %s\n", preferred_apps);
        pthread_cond_signal(&ux_trigger_cond);
    }
    else if (msg->opcode == UXE_TRIGGER_LIST_FAV_APPS) {
        //暂未使用
        ...
    }
    break;
}

* vendor/qcom/proprietary/android-perf/io-p/io-prefetch/uba.cpp
int get_preferred_apps(char **out_list, int disable_palm, char * all_week_day, int all_time_slot, bool launch)
{
    long cur_time = (long) now_secs();
    char *week_day = NULL;
    pkg_ux_top_details *cur_list = NULL;
    int total_pkgs = 0, index = 0, table_size = 0, num_pkgs = 0;
    //临时个数为18
    int tmp_size = PREFERRED_APP_COUNT + PREFERRED_APP_COUNT/2;
    //week_day为true或者false
    week_day = (char *) malloc(6*sizeof(char));
    if (week_day == NULL) {
        //Malloc failed. Most-probably low on memory.
        goto out;
    }
    //计算week_day:如果时周二~周六,则为true,否则为false
    //time_slot:4~12时,为1
    //12~17时,为2
    //17~21时,为3
    //0~4,21~24,为4
    compute_time_day_slot(week_day, &time_slot);
    //获取ux_pkg_tbl中应用个数
    total_pkgs = get_total_ux_pkgs(0);

    if (total_pkgs == 0) {
        goto out;
    }
    cur_list = (pkg_ux_top_details *) malloc ((tmp_size+1) * sizeof(pkg_ux_top_details));

    if (cur_list == NULL) {
        goto out;
    }

    QLOGI("Get_Preferred_Apps: Current Time stats: Week_day: %s, Time_slot: %d, Total_pkgs: %d\n", week_day, time_slot, total_pkgs);
    //根据time_slot获取ux_pkg_tbl中应用列表,即所有记录的应用的binderApplication时间,打开次数等信息
    if (disable_palm == 1 && all_week_day != NULL && all_time_slot != -1)
        num_pkgs = get_top_ux_pkg_list(cur_list, all_week_day, all_time_slot, tmp_size, 1);
    else
        num_pkgs = get_top_ux_pkg_list(cur_list, week_day, time_slot, tmp_size, 1);
    QLOGI("Get_Preferred_Apps: Get Top pkg list Returned pkgs: %d\n", num_pkgs);

    if (num_pkgs == 0) {
        goto out;
    }

    for (index = 0 ; (index < PREFERRED_APP_COUNT && index < num_pkgs) ; index++) {
         int total_count = cur_list[index].num_launches;
         int tslot_count = cur_list[index].timeslot_count_select;
         QLOGI("Get_Preferred_Apps: Accessing cur list: %s pkg_launch_count: %d bindApp_dur: %d disAct_dur%d\n", cur_list[index].pkg_name, cur_list[index].num_launches,
                                                                                                                cur_list[index].bindApp_dur, cur_list[index].disAct_dur);
         strlcpy(out_list[index], cur_list[index].pkg_name, PKG_NAME_LEN);
         QLOGI("Get_Preferred_Apps: Accessing copied list: %s\n", out_list[index]);
         table_size++;
    }

    if (disable_palm == 0) {
        QLOGI("Get_Preferred_Apps: Accessing table_size %d\n", table_size);
        set_palm_table(out_list, table_size, cur_time, launch);
    }
    else if (disable_palm == 1) {
        QLOGI("Get_Preferred_Apps: Return all Fav apps List with size = %d\n", table_size);
    }

out:
    if (cur_list != NULL) {
        free(cur_list);
    }
    if (week_day != NULL) {
        free(week_day);
    }
    return table_size;
}

调用逻辑为:清除后台任务

ActivityStackSupervisor::removeTaskByIdLocked----ActivityStackSupervisor::cleanUpRemovedTaskLocked-------PreferredAppsTask::doInBackground-------------BoostFramework::perfUXEngine_trigger

切换到launcher界面

ActivityStack::resumeTopActivityInnerLocked----PreferredAppsTask::doInBackground

切换到home或者进入任务列表

ActivityRecord::completeResumeLocked----PreferredAppsTask::doInBackground

get_preferred_apps根据应用的启动时间和星期几,来预测要启动的应用,DB中只存储12个应用当通过perfUXEngine_trigger获取到应用信息后,调用startActivityAsUserEmpty来启动一个空的应用,在再次启动时,直接跳过binderApplication阶段,从而加快启动速度,这里startActivityAsUserEmpty直接调用startProcessLocked创建一个进程,并执行bindApplication流程,因为startProcessLocked时,指定启动空activity(传入new HostingRecord(null)),从而在RootActivityContainer::attachApplication时,不在调用realStartActivityLocked

* frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
class PreferredAppsTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            String res = null;
            final Intent intent = new Intent(Intent.ACTION_MAIN);
            int trimLevel = 0;
            try {
                trimLevel = ActivityManager.getService().getMemoryTrimLevel();
            } catch (RemoteException e) {
                return null;
            }
            if (mUxPerf != null
                   && trimLevel < ProcessStats.ADJ_MEM_FACTOR_CRITICAL) {
                res = mUxPerf.perfUXEngine_trigger(BoostFramework.UXE_TRIGGER); 
                if (res == null)
                    return null;
                String[] p_apps = res.split("/");
                if (p_apps.length != 0) {
                    ArrayList<String> apps_l = new ArrayList(Arrays.asList(p_apps));
                    Bundle bParams = new Bundle();
                    if (bParams == null)
                        return null;
                    bParams.putStringArrayList("start_empty_apps", apps_l);
                    final Message msg = PooledLambda.obtainMessage(
                                            ActivityManagerInternal::startActivityAsUserEmpty, mService.mAmInternal, bParams);
                    mService.mH.sendMessage(msg);
                }
            }
            return null;
        }
    }

get_preferred_apps的主要流程为:

  1. 调用compute_time_day_slot计算当前是否工作日和时间段

  2. 根据工作日和时间段调用get_top_ux_pkg_list获取ux_pkg_tbl和ux_lat_tbl中的应用启动次数(pkg_use_count),启动时间(bindApp_dur、disAct_dur)、时间段启动次数(timeslot_count_)等信息

  3. 获取到最大PREFERRED_APP_COUNT(12)个应用信息,放入out_list

  4. 调用set_palm_table进行应用信息过滤,并更新tracker列表

    int set_palm_table(char **list, int tb_size, long cur_time, bool launch) {
    //Compare old and new list, and send update to PALM
    //Check for empty conditions.
    int i = 0, j = 0, match = 0, match_cached = 0;

     palm_table *cur_tracker;
     palm_table *tmp_tracker;
     cur_tracker = (palm_table *) malloc (tb_size * sizeof(palm_table));
    
     if (cur_tracker == NULL) {
         QLOGE("Set_Palm_Table: No memory to set temp table. Return");
         return 0;
     }
    
     QLOGI("Set_Palm_Table: Entry, Table size: %d, old table size: %d, Current time: %ld \n", tb_size, old_tbl_size, cur_time);
     for(i = 0; i < tb_size; i++)
     {
          strlcpy(cur_tracker[i].pkg_name, list[i], PKG_NAME_LEN);
          cur_tracker[i].cached = launch;
          cur_tracker[i].empty = !launch;
          cur_tracker[i].memory_rss = 0; //Use getMemory or db call later on.
          cur_tracker[i].last_empty_time = cur_time;
          cur_tracker[i].do_not_launch = false;
          cur_tracker[i].hit = false;
          cur_tracker[i].comp = false;
          QLOGI("Set_Palm_Table: %s", cur_tracker[i].pkg_name);
          for(j = 0; j < old_tbl_size; j++)
          {
              if(!strncmp(tracker[j].pkg_name, list[i], PKG_NAME_LEN))
              {
                  cur_tracker[i].memory_rss = tracker[j].memory_rss;
                  cur_tracker[i].last_empty_time = tracker[j].last_empty_time;
                  cur_tracker[i].cached = tracker[j].cached;
                  cur_tracker[i].empty = tracker[j].empty;
                  cur_tracker[i].do_not_launch = tracker[j].do_not_launch;
                  cur_tracker[i].hit = tracker[j].hit;
                  tracker[j].comp = true;
                  strlcpy(cur_tracker[i].pkg_name, tracker[j].pkg_name, PKG_NAME_LEN);
                  //
                  if(launch || tracker[j].empty || tracker[j].cached) {
                      list[i][0] = 0;
                      list[i][1] = '\0';
                      QLOGI("Set_Palm_Table: Matched empty from previous table. Pkg_name: %s Pkg State--Cached: %d, Empty: %d\n", tracker[j].pkg_name, tracker[j].cached, tracker[j].empty);
                      strlcpy(cur_tracker[i].pkg_name, tracker[j].pkg_name, PKG_NAME_LEN);
                      match++;
                  }
                 //如果应用2s内被杀两次(do_not_launch为true)
                  else if(tracker[j].do_not_launch) {
                      QLOGI("Set_Palm_Table: Not allowed for launch due to looping: %s\n", tracker[j].pkg_name);
                      list[i][0] = 0;
                      list[i][1] = '\0';
                      cur_tracker[i].cached = false;
                      cur_tracker[i].empty = false;
                      cur_tracker[i].do_not_launch = true;
                      if ((cur_time - tracker[j].last_empty_time) > empty_restart_threshold) {
                          cur_tracker[i].do_not_launch = false;
                          QLOGE("Set_Palm_Table: Timeout expired. Allowed to start as empty: %s\n", tracker[j].pkg_name);
                      }
                  }
                  //应用被杀后,再次返回home会执行此流程,被拉起,将empty付为空
                  else {
                      cur_tracker[i].empty = true;
                      cur_tracker[i].cached = false;
                      QLOGI("Set_Palm_Table: Matched, but app was killed. Resetting. Pkg_name: %s Pkg State--Cached: %d, Empty: %d\n", cur_tracker[i].pkg_name, cur_tracker[i].cached, cur_tracker[i].empty);
                  }
              }
          }
     }
     old_tbl_size = tb_size;
     //memcpy(tracker, cur_tracker, (tb_size*sizeof(palm_table)));
     tmp_tracker = tracker;
     tracker = cur_tracker;
     free(tmp_tracker);
     if (match == tb_size)
         return 0;
     else
         return (tb_size - match);
    

    }

set_palm_table

  1. 根据最新的可能要启动应用列表,构建一个最新的tracker

  2. 如果应用displayed完成,lunch为true,返回home,lunch为false

  3. 当应用kill时,被置为true,当返回home时,进入set_palm_table,然后走else流程,将empty置为true,然后被拉起,empty为true

  4. 如果应用2s内被杀两次,则do_not_launch为true

  5. 如果应用已启动,则cached为true

  6. 上述四种情况,则从out列表移除

  7. 如果是当前时间段不再tracker的应用(comp为false),(比如工作日/非工作日切换,常用应用个数大于12个时候,使用次数变更到底等),则如果已经启动,更新预言成功(predict_success)权重+10,否则更新预言失败(predict_fail)权重pr_launch/PREFERRED_APP_COUNT*10。这里的predict_success和predict_fail暂未使用,在android R中不在使用

标签:name,list,bindApp,源码,Prefetch,ux,android,tracker,pkg
From: https://www.cnblogs.com/linhaostudy/p/18337059

相关文章

  • Android TV上Recyclerview焦点控制心得
    背景:项目里有一个定时刷新的需求,刷新的数据是填充在Recyclerview里的 问题:用户可能已经滑动Recyclerview到某一位置,这时候触发了定时刷新任务,新的数据到来会触发Recyclerview的adapter.notifydatasetchanged(),这时候1.数据已经刷新,Recyclerview应该会滑动到初始位置2.Recyc......
  • PGjdbc源码试读(一)
    目标通过追踪常用的jdbc方法去熟悉PGjdbc的执行流程常见jdbc使用流程Class.forName("org.postgresql.Driver");ConnectionconnectionPG=DriverManager.getConnection("jdbc:postgresql://localhost:5432/xxx","xxxxxx","xxxxxx");Statementstatement......
  • single-spa 源码解析
    single-spa源码解析single-spa是一种微前端的实现方案。阿里的qiankun其实是基于这个项目做了二次开发,其实是做了个拓展,提供了html解析与js沙盒两个功能。本文从single-spa的代码实现角度解析一下它的实现原理。前提假设single-spa首先要求每个子应用需要提供bootstrap,mount,......
  • JSP学生宿舍管理系统599o3(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
    系统程序文件列表用户功能:宿管,学生,班级,宿舍楼宇,宿舍分配,来访登记开题报告内容JSP学生宿舍管理系统开题报告一、选题背景与意义1.选题背景随着我国经济的飞速发展和科学技术的全面进步,以计算机技术、通信技术和数据库技术为基础的信息系统正处于蓬勃发展的黄金时......
  • JSP学生宿舍管理系统66a58(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
    系统程序文件列表项目功能:学生,公寓信息,公寓类型,寝室类型,寝室信息,卫生检查,住宿信息,充值信息,违纪信息,学生账户开题报告内容JSP学生宿舍管理系统开题报告一、引言1.1研究背景随着高校招生规模的不断扩大,学生宿舍管理日益成为高校后勤工作的重要组成部分。传统的......
  • JSP学生宿舍管理系统24fu4(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
    系统程序文件列表项目功能:学生,宿舍信息,卫生检查,电费信息,报修申请,换寝申请,寄存申请,留言信息,检修信息,检修评价,公告信息开题报告内容JSP学生宿舍管理系统 开题报告一、引言1.1研究背景随着高校教育事业的快速发展,学生宿舍作为学生在校期间的重要生活空间,其管......
  • JSP学生宿舍管理系统2z933(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
    系统程序文件列表项目功能:班级,学生,维修人员,宿舍公告,宿舍信息,宿舍安排,报修评价,报修处理,报修信息,在线咨询开题报告内容JSP学生宿舍管理系统 开题报告一、引言1.1研究背景随着高校招生规模的不断扩大,学生宿舍管理面临着诸多挑战,如宿舍分配、日常管理、安全监控......
  • Android开发 - (适配器)Adapter类中SimpleAdapter实现类详细解析
    具体作用SimpleAdapter的主要作用是简化将数据源(如List<Map<String,Object>>)绑定到视图组件(如TextView、ImageView等)的过程。它可以根据指定的键将数据映射到指定的视图组件上,从而快速实现数据的展示参数、方法解析SimpleAdapter(Contextcontext,List<?extendsMap......
  • 基于Django的超市小程序+47822(免费领源码)可做计算机毕业设计JAVA、PHP、爬虫、APP、小
    基于django超市小程序摘 要随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,超市小程序被用户普遍使用,为方便用户能够可以随时进行超市小程序的数据信息管理,特开发了基于djan......
  • android.uid.system sendBroadcast失效的问题
    如果是系统应用android:sharedUserId="android.uid.system"报这个错 Callingamethodinthesystemprocesswithoutaqualifieduser:android.app.ContextImpl.sendBroadcast:1188android.content.ContextWrapper.sendBroadcast:解决添加如下权限<uses-permissionandroi......