Android system_other目录包含了odex、vdex文件,该目录是如何挂载的?

system_other目录存放着 odex、vdex文件

 

 

这里是在PSM初始化时,PMS_scan_START之前进行copy动作,且只在首次开机执行。

有开关控制此功能:ro.cp_system_other_odex=1则打开,默认为0

如注释中提到的,PMS并不负责真正的copy,因为它没有相关权限,而是由init进程来执行。

PMS和init的交互是通过系统属性:sys.cppreopt

PMS将其设置为requested,来请求init去copy;每隔一段时间检测下是否已经变更为finished。

当sys.cppreopt=requested时,触发copy;结束后,设置为finished

如下是对应rc文件,相关操作会在init进程中执行。

这个代码给我们提供了一个普通进程和init进程交互的方法:透过property通信

 

 

 

个人总结:

1、首次开机时,当ro.cp_system_other_odex为1时,PMS将sys.cppreopt设置requested,触发cppreopts.rc将system_other挂载到 /postinstall
2、cppreopts.sh将挂载点/postinstall中的 *.odex、*.vdex和*.art类型文件拷贝到 /data/dalvik-cache/
3、*.odex、*.vdex和*.art类型文件拷贝完成后,cppreopts.rc 将挂载点( /postinstall) 进行卸载

相关源文件:
system/extras/cppreopts/cppreopts.rc
system/extras/cppreopts/cppreopts.sh
system/extras/preopt2cachename/preopt2cachename.cpp

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

    if (!mOnlyCore && mFirstBoot) {
        requestCopyPreoptedFiles(mInjector);
    }

    /**
     * Requests that files preopted on a secondary system partition be copied to the data partition
     * if possible.  Note that the actual copying of the files is accomplished by init for security
     * reasons. This simply requests that the copy takes place and awaits confirmation of its
     * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
     */
    private static void requestCopyPreoptedFiles(Injector injector) {
        final int WAIT_TIME_MS = 100;
        final String CP_PREOPT_PROPERTY = "sys.cppreopt";
        if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
            SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
            // We will wait for up to 100 seconds.
            final long timeStart = SystemClock.uptimeMillis();
            final long timeEnd = timeStart + 100 * 1000;
            long timeNow = timeStart;
            while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
                try {
                    Thread.sleep(WAIT_TIME_MS);
                } catch (InterruptedException e) {
                    // Do nothing
                }
                timeNow = SystemClock.uptimeMillis();
                if (timeNow > timeEnd) {
                    SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
                    Slog.wtf(TAG, "cppreopt did not finish!");
                    break;
                }
            }

            Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
        }
    }
    
    
    
# /system/extras/cppreopts/cppreopts.rc

# Copyright 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

service cppreopts /system/bin/cppreopts.sh /postinstall
    oneshot
    disabled
    user root
    priority -20
    ioprio rt 0
    writepid /dev/cpuset/foreground/tasks
    capabilities

# Post install is above Treble VINTF, because it runs some utilities from
# /system. Therefore, the fstab can only be in either /system or /product.
on property:sys.cppreopt=requested && property:ro.postinstall.fstab.prefix=/system
    mount_all /system/etc/fstab.postinstall
    exec_start cppreopts
    # Optional script to copy additional preloaded content to data directory
    exec - system system -- /system/bin/preloads_copy.sh /postinstall
    umount_all /system/etc/fstab.postinstall
    setprop sys.cppreopt finished

on property:sys.cppreopt=requested && property:ro.postinstall.fstab.prefix=/product
    mount_all /product/etc/fstab.postinstall
    exec_start cppreopts
    # Optional script to copy additional preloaded content to data directory
    exec - system system -- /system/bin/preloads_copy.sh /postinstall
    umount_all /product/etc/fstab.postinstall
    setprop sys.cppreopt finished









# /system/extras/cppreopts/preloads_copy.sh
#!/system/bin/sh
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This script copies preloaded content from system_other to data partition

# create files with 644 (global read) permissions.
umask 022

if [ $# -eq 1 ]; then
  # Where system_other is mounted that contains the preloads dir
  mountpoint=$1
  dest_dir=/data/preloads
  log -p i -t preloads_copy "Copying from $mountpoint/preloads"
  # Parallelize by copying subfolders and files in the background
  for file in $(find ${mountpoint}/preloads -mindepth 1 -maxdepth 1); do
      cp -rn $file $dest_dir &
  done
  # Wait for jobs to finish
  wait
  log -p i -t preloads_copy "Copying complete"
  exit 0
else
  log -p e -t preloads_copy "Usage: preloads_copy.sh <system_other-mount-point>"
  exit 1
fi




说明:
# system/extras/cppreopts/cppreopts.sh    
#!/system/bin/sh
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# create files with 644 (global read) permissions.
umask 022

# Helper function to copy files
function do_copy() {
  source_file=$1
  dest_name=$2
  # Move to a temporary file so we can do a rename and have the preopted file
  # appear atomically in the filesystem.
  temp_dest_name=${dest_name}.tmp
  if ! cp ${source_file} ${temp_dest_name} ; then
    log -p w -t cppreopts "Unable to copy file ${source_file} to ${temp_dest_name}!"
  else
    log -p i -t cppreopts "Copied file from ${source_file} to ${temp_dest_name}"
    fsync ${temp_dest_name}
    if ! mv ${temp_dest_name} ${dest_name} ; then
      log -p w -t cppreopts "Unable to rename temporary file from ${temp_dest_name} to ${dest_name}"
      rm ${temp_dest_name} || log -p w -t cppreopts "Unable to remove temporary file ${temp_dest_name}"
    else
      fsync ${dest_name}
      log -p i -t cppreopts "Renamed temporary file from ${temp_dest_name} to ${dest_name}"
    fi
  fi
}

if [ $# -eq 1 ]; then
  # Where the system_b is mounted that contains the preopt'd files
  mountpoint=$1

  if ! test -f ${mountpoint}/system-other-odex-marker ; then
    log -p i -t cppreopts "system_other partition does not appear to have been built to contain preopted files."
    exit 1
  fi

  log -p i -t cppreopts "cppreopts from ${mountpoint}"
  # For each odex and vdex file do the copy task
  # NOTE: this implementation will break in any path with spaces to favor
  # background copy tasks
  for file in $(find ${mountpoint} -type f -name "*.odex" -o -type f -name "*.vdex" -o -type f -name "*.art"); do
    real_name=${file/${mountpoint}/}
    dest_name=$(preopt2cachename ${real_name})
    if ! test $? -eq 0 ; then
      log -p i -t cppreopts "Unable to figure out destination for ${file}"
      continue
    fi
    # Copy files in background to speed things up
    do_copy ${file} ${dest_name} &
  done
  # Wait for jobs to finish
  wait
  exit 0
else
  log -p e -t cppreopts "Usage: cppreopts <preopts-mount-point>"
  exit 1
fi




#/system/extras/preopt2cachename/preopt2cachename.cpp
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>

#include <android-base/logging.h>
#include <android-base/strings.h>

#ifndef LOG_TAG
#define LOG_TAG "preopt2cachename"
#endif

static const char* kDalvikCacheDir = "/data/dalvik-cache/";
static const char* kOdexCacheSuffix = "@classes.dex";
static const char* kVdexCacheSuffix = "@classes.vdex";
static const char* kArtCacheSuffix = "@classes.art";

// Returns the ISA extracted from the file_location. file_location is formatted like
// /system{/product,}/{priv-,}app/<app_name>/oat/<isa>/<app_name>.{odex,vdex} for all functions. We
// return an empty string "" in error cases.
static std::string ExtractISA(const std::string& file_location) {
  std::vector<std::string> split_file_location = android::base::Split(file_location, "/");
  if (split_file_location.size() <= 1) {
    return "";
  } else if (split_file_location.size() != 7 && split_file_location.size() != 8) {
    LOG(WARNING) << "Unexpected length for file-location. We expected 7 or 8 segments but found "
                 << split_file_location.size() << " for " << file_location;
  }
  return split_file_location[split_file_location.size() - 2];
}

// Returns the apk name extracted from the file_location.
// file_location is formatted like /system/app/<app_name>/oat/<isa>/<app_name>.{odex,vdex}.
// We return the final <app_name> with the .{odex,vdex} replaced with .apk.
static std::string ExtractAPKName(const std::string& file_location) {
  // Find and copy filename.
  size_t file_location_start = file_location.rfind('/');
  if (file_location_start == std::string::npos) {
    return "";
  }
  size_t ext_start = file_location.rfind('.');
  if (ext_start == std::string::npos || ext_start < file_location_start) {
    return "";
  }
  std::string apk_name = file_location.substr(file_location_start + 1,
                                              ext_start - file_location_start);

  // Replace extension with .apk.
  apk_name += "apk";
  return apk_name;
}

// The cache file name is /data/dalvik-cache/<isa>/ prior to this function
static bool SystemBFilenameToCacheFile(const std::string& file_location,
                                       /*in-out*/std::string& cache_file) {
  // Skip the first '/' in file_location.
  size_t initial_position = file_location[0] == '/' ? 1 : 0;
  size_t apk_position = file_location.find("/oat", initial_position);
  if (apk_position == std::string::npos) {
    LOG(ERROR) << "Unable to find oat directory!";
    return false;
  }

  size_t cache_file_position = cache_file.size();
  cache_file += file_location.substr(initial_position, apk_position);
  // '/' -> '@' up to where the apk would be.
  cache_file_position = cache_file.find('/', cache_file_position);
  while (cache_file_position != std::string::npos) {
    cache_file[cache_file_position] = '@';
    cache_file_position = cache_file.find('/', cache_file_position);
  }

  // Add <apk_name>.
  std::string apk_name = ExtractAPKName(file_location);
  if (apk_name.empty()) {
    LOG(ERROR) << "Unable to determine apk name from file name '" << file_location << "'";
    return false;
  }
  std::string::size_type pos = file_location.find_last_of('.');
  if (pos == std::string::npos) {
    LOG(ERROR) << "Invalid file location '" << file_location << "'";
    return false;
  }
  cache_file += apk_name;
  std::string extension(file_location.substr(pos));
  if (extension == ".vdex") {
    cache_file += kVdexCacheSuffix;
  } else if (extension == ".art") {
    cache_file += kArtCacheSuffix;
  } else {
    cache_file += kOdexCacheSuffix;
  }
  return true;
}

// Do the overall transformation from file_location to output_file_location. Prior to this the
// output_file_location is empty.
static bool SystemBFileToCacheFile(const std::string& file_location,
                                   /*out*/std::string& output_file_location) {
  std::string isa = ExtractISA(file_location);
  if (isa.empty()) {
    LOG(ERROR) << "Unable to determine isa for file '" << file_location << "', skipping";
    return false;
  }
  output_file_location += isa;
  output_file_location += '/';
  return SystemBFilenameToCacheFile(file_location, output_file_location);
}

// This program is used to determine where in the /data directory the runtime will search for an
// odex file if it is unable to find one at the given 'preopt-name' location. This is used to allow
// us to store these preopted files in the unused system_b partition and copy them out on first
// boot of the device.
int main(int argc, char *argv[]) {
  if (argc != 2) {
    LOG(ERROR) << "usage: preopt2cachename preopt-location";
    return 2;
  }
  std::string file_location(argv[1]);
  std::string output_file_location(kDalvikCacheDir);
  if (!SystemBFileToCacheFile(file_location, output_file_location)) {
    return 1;
  } else {
    std::cout << output_file_location;
  }
  return 0;
}

  

 

posted @ 2022-09-21 15:23  行走的思想  阅读(27)  评论(0)    收藏  举报