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

浙公网安备 33010602011771号