之前两篇博客分析了三方应用如何安装的问题,但是两个方法都有问题
1.调用接口安装的,可能Launcher启动后还没安装完。
2.而copy到data/app下又会有两份apk问题。
这篇博客我们用另一种方法,就是放在system/third_app下,开机的时候直接扫描这个目录。然后我们在data/system下建一个xml文件,当应用卸载的时候,我们再xml上记录该应用被卸载了,当再次开机的时候,扫描到该应用就直接跳过。而当恢复出厂设置时data目录重置,xml文件被删除。system/third_app又会被重新扫描,所有的apk就全部安装上了,而当我们卸载时,因为system/third_app的权限问题,PKMS删除不了,正好恢复出厂设置的时候可以重新恢复。
下面我们就来看代码,首先我们在PKMS中新建了两个成员变量,mVendorPackages记录所有system/third_app下的apk,VendorSettings是模仿PKMS的mSettings,也是用来保存xml文件的。
+ final HashMap<String, PackageParser.Package> mVendorPackages =
+ new HashMap<String, PackageParser.Package>();
final Settings mSettings;
+ final VendorSettings mVendorSettings;
boolean mRestoredSettings;
一、构造函数
然后在PKMS的构造函数中修改如下代码,新建VendorSettings对象,然后哦扫描system/third_app,并且传入参数PackageParser.PARSE_IS_VENDOR。我们来看下构造函数的部分diff文件
public class PackageManagerService extends IPackageManager.Stub {
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
+ mVendorSettings = new VendorSettings();
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -2015,6 +2020,8 @@ public class PackageManagerService extends IPackageManager.Stub {
mCustomResolverComponentName = ComponentName.unflattenFromString(
customResolverActivity);
}
+ //get vendor package info
+ mVendorSettings.readLPw();//读取xml内容到mVendorSettings中
long startTime = SystemClock.uptimeMillis();
@@ -2194,6 +2201,11 @@ public class PackageManagerService extends IPackageManager.Stub {
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+ final File operatorAppDir = new File("/system/third_app");
+
+ //Add PARSE_IS_VENDOR for operator apps
+ scanDirLI(operatorAppDir, PackageParser.PARSE_IS_VENDOR, scanFlags, 0);
PackageParser.PARSE_IS_VENDOR是我们再PackageParser中新建的
public final static int PARSE_IS_VENDOR = 1<<10;
我们再来看构造函数下面这段代码,系统应用在扫描完目录之后,需要对mSettings的mPackages进行删减,而system/third_app也属于这个范畴。
if (!mOnlyCore) {
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
/*
* If this is not a system app, it can't be a
* disable system app.
*/
/*if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}*/
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0//系统app、我们的三方app需要删减
//Vendor apps are belong to system domain, therefore, need prune.
&& (ps.pkgFlags & ApplicationInfo.PRIVATE_FLAG_UNINSTALL_APP) == 0) {
continue;
}
/*
* If the package is scanned, it's not erased.
*/
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
/*
* If the system app is both scanned and in the
* disabled packages list, then it must have been
* added via OTA. Remove it from the currently
* scanned package so the previously user-installed
* application can be scanned.
*/
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN, "Expecting better updated system app for "
+ ps.name + "; removing system app. Last known codePath="
+ ps.codePathString + ", installStatus=" + ps.installStatus
+ ", versionCode=" + ps.versionCode + "; scanned versionCode="
+ scannedPkg.mVersionCode);
removePackageLI(ps, true);
mExpectingBetter.put(ps.name, ps.codePath);
}
continue;
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; wiping its data");
removeDataDirsLI(null, ps.name);
} else {
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
}
Iterator<VendorPackageSettings> vpsit = mVendorSettings.mVendorPackages.values().iterator();
while (vpsit.hasNext()) {//遍历mVendorSettings
VendorPackageSettings vps = vpsit.next();
final PackageParser.Package scannedVendorPkg = mVendorPackages.get(vps.getPackageName());
if (scannedVendorPkg == null) {
vpsit.remove();
Slog.w(TAG, "Vendor package: " + vps.getPackageName()
+ " has been removed from system");
}
}
扫描完system/third_app后mVendorPackages就有所有该目录下的apk了,mVendorSettings中业余该目录有所有的三方app了。但是mVendorSettings中的值也有xml中的pkg,但是xml的pkg可能实际已经被删除了。这个时候我们需要看看这个mVendorSettings中的pkg是否在mVendorPackages(PKMS扫描system /third_app的pkg)中有,如果没有了说明被删除了,我们也要讲这个pkg从xml中删除。
当然在PKMS的最后,我们也要像mSettings一样调用mVendorSettings.writeLPr函数来更新xml文件。
mSettings.writeLPr();
+ mVendorSettings.writeLPr();
二、扫描system/third_app目录
扫描目录的函数是scanDirLI,后面会调用scanPackageLI函数,这个scanPackageLI函数是参数为File的那个,我们来看下面这段代码,当我们调用PackageParser的parsePackage函数来解析apk文件之后,我们把有PackageParser.PARSE_IS_VENDOR flag的pkg放入mVendorPackages代表这是扫描的system/third_app目录。然后当有PackageParser.PARSE_IS_VENDOR 的这个flag时,我们再去看mVendorSettings中是否有这个pkg,如果有,而且是uninstalled状态,就直接return null,结束安装。
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {
if (mVendorPackages.get(pkg.packageName) == null) {
mVendorPackages.put(pkg.packageName, pkg);//放入mVendorPackages
}
}
//Check whether we should skip the scan of current package
//We should only check vendor packages
if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {
VendorPackageSettings vps = mVendorSettings.mVendorPackages.get(pkg.packageName);//扫描mVendorSettings的pkg
if (vps != null) {
if (!vps.getIntallStatus()) {
//Skip the vendor package that was uninstalled by user
Log.i(TAG, "Package " + vps.getPackageName()+ " skipped due to uninstalled");
return null;
}
}
}
扫描目录安装最后会调用scanPackageDirtyLI函数,我们在这个函数一开始当有PackageParser.PARSE_IS_VENDOR这个flag,其pkg的applicationInfo.flags |= ApplicationInfo.PRIVATE_FLAG_UNINSTALL_APP
if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
if ((parseFlags&PackageParser.PARSE_IS_VENDOR) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.PRIVATE_FLAG_UNINSTALL_APP;
}
继续这个函数,在writer部分,当有PackageParser.PARSE_IS_VENDOR的flag,我们把这个pkg放入mVendorSettings中,并且其状态为true。最后扫描完会更新其xml文件。
// writer
synchronized (mPackages) {
// We don't expect installation to fail beyond this point
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
final Iterator<PackageCleanItem> iter = mSettings.mPackagesToBeCleaned.iterator();
while (iter.hasNext()) {
PackageCleanItem item = iter.next();
if (pkgName.equals(item.packageName)) {
iter.remove();
}
}
//If the newly installed package is vendor app,
//add or update it in vendor settings
if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {
mVendorSettings.insertPackage(pkg.packageName,true);
}
三、删除应用
最后我们再看下删除应用的情况,删除应用最后会调用removePackageDataLI函数,我们来看下这个函数的diff文件
public class PackageManagerService extends IPackageManager.Stub {
removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
final PackageSetting deletedPs;
+ final VendorPackageSettings delVps;
// reader
synchronized (mPackages) {
deletedPs = mSettings.mPackages.get(packageName);
+ delVps = mVendorSettings.mVendorPackages.get(packageName);//需要在mVendorSettings中删除的应用
if (outInfo != null) {
outInfo.removedPackage = packageName;
outInfo.removedUsers = deletedPs != null
@@ -13268,6 +13326,12 @@ public class PackageManagerService extends IPackageManager.Stub {
// from KeyStore.
removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);
}
+ if (delVps != null) {
+ //If the deleted package is vendor package
+ //remove it from vendor settins
+ mVendorSettings.setPackageStatus(packageName, false);//设置其删除应用状态为uninstalled
+ mVendorSettings.writeLPr();//更新到xml中
+ }
}
四、VendorSettings类
下面我们再来看下VendorSettings 类是模仿PKMS的Settings类,更新xml文件的。
/*
* Copyright (C) 2008 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.
*/
package com.android.server.pm;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import com.android.internal.util.FastXmlSerializer;
import android.os.Environment;
import android.os.FileUtils;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;
final class VendorSettings {
private static final String TAG_ROOT = "packages";
private static final String TAG_PACKAGE = "package";
static final String ATTR_PACKAGE_NAME = "name";
static final String ATTR_INSTALL_STATUS = "installStatus";
static final String VAL_INSTALLED = "installed";
static final String VAL_UNINSTALLED = "uninstalled";
private final File mSystemDir;
private final File mVendorSettingsFilename;
private final File mVendorBackupSettingsFilename;
final HashMap<String, VendorPackageSettings> mVendorPackages =
new HashMap<String, VendorPackageSettings>();
VendorSettings() {
this(Environment.getDataDirectory());
}
VendorSettings(File dataDir) {
mSystemDir = new File(dataDir, "system");;
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
mVendorSettingsFilename = new File(mSystemDir, "custom-packages.xml");//xml文件
mVendorBackupSettingsFilename = new File(mSystemDir, "custom-packages-backup.xml");//xml备份文件
}
void insertPackage(String packageName, boolean installStatus) {
VendorPackageSettings vps = mVendorPackages.get(packageName);
if (vps != null) {
vps.setIntallStatus(installStatus);
} else {
vps = new VendorPackageSettings(packageName, installStatus);
mVendorPackages.put(packageName, vps);
}
}
void setPackageStatus(String packageName, boolean installStatus) {
VendorPackageSettings vps = mVendorPackages.get(packageName);
if (vps == null) {
//Shall we return a much meaningful result?
return;
} else {
vps.setIntallStatus(installStatus);
}
}
void removePackage(String packageName) {
if (mVendorPackages.get(packageName) != null) {
mVendorPackages.remove(packageName);
}
}
void readLPw() {
FileInputStream str = null;
DocumentBuilderFactory docBuilderFactory = null;
DocumentBuilder docBuilder = null;
Document doc = null;
if (mVendorBackupSettingsFilename.exists()) {//先看备份是否有备份文件,有代表之前写的时候失败了,就要使用备份文件
try {
str = new FileInputStream(mVendorBackupSettingsFilename);
if (mVendorSettingsFilename.exists()) {
//If both the backup and vendor settings file exist, we
//ignore the settings since it might have been corrupted.
Slog.w(PackageManagerService.TAG, "Cleaning up settings file");
mVendorSettingsFilename.delete();
}
} catch (java.io.IOException e) {
}
}
try {
if (str == null) {
if (!mVendorSettingsFilename.exists()) {
return;
}
str = new FileInputStream(mVendorSettingsFilename);
}
docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilder = docBuilderFactory.newDocumentBuilder();
doc = docBuilder.parse(str);
Element root = doc.getDocumentElement();
NodeList nodeList = root.getElementsByTagName(TAG_PACKAGE);
Node node = null;
NamedNodeMap nodeMap = null;
String packageName = null;
String installStatus = null;
for (int i = 0; i < nodeList.getLength(); i++) {//读取xml文件内容到mVendorPackages中
node = nodeList.item(i);
if (node.getNodeName().equals(TAG_PACKAGE)) {
nodeMap = node.getAttributes();
packageName = nodeMap.getNamedItem(ATTR_PACKAGE_NAME).getTextContent();
installStatus = nodeMap.getNamedItem(ATTR_INSTALL_STATUS).getTextContent();
mVendorPackages.put(packageName,
new VendorPackageSettings(packageName, installStatus.equals(VAL_INSTALLED)));
}
}
} catch (java.io.IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
void writeLPr() {
if (mVendorSettingsFilename.exists()) {
if (!mVendorBackupSettingsFilename.exists()) {//创建备份文件类似mSettings的处理方法
if (!mVendorSettingsFilename.renameTo(mVendorBackupSettingsFilename)) {
Slog.e(PackageManagerService.TAG, "Unable to backup package manager vendor settings, "
+ " current changes will be lost at reboot");
return;
}
} else {
mVendorSettingsFilename.delete();
Slog.w(PackageManagerService.TAG, "Preserving older vendor settings backup");
}
}
try {
FileOutputStream fstr = new FileOutputStream(mVendorSettingsFilename);
XmlSerializer serializer = new FastXmlSerializer();
//XmlSerializer serializer = Xml.newSerializer()
BufferedOutputStream str = new BufferedOutputStream(fstr);
serializer.setOutput(str, "utf-8");
serializer.startDocument(null, true);
serializer.startTag(null, TAG_ROOT);
for (VendorPackageSettings ps : mVendorPackages.values()) {//将mVendorPackages中的状态写入xml文件
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_PACKAGE_NAME, ps.getPackageName());
serializer.attribute(null, ATTR_INSTALL_STATUS,
ps.getIntallStatus() ? VAL_INSTALLED : VAL_UNINSTALLED);
serializer.endTag(null, TAG_PACKAGE);
}
serializer.endTag(null, TAG_ROOT);
serializer.endDocument();
str.flush();
FileUtils.sync(fstr);
str.close();
mVendorBackupSettingsFilename.delete();
FileUtils.setPermissions(mVendorSettingsFilename.toString(),
FileUtils.S_IRUSR|FileUtils.S_IWUSR
|FileUtils.S_IRGRP|FileUtils.S_IWGRP,
-1, -1);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
VendorPackageSettings 是描述每个pkg安装的状态。
/*
* Copyright (C) 2008 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.
*/
package com.android.server.pm;
final class VendorPackageSettings {
final String mPackageName;
boolean mIntallStatus = true;
VendorPackageSettings(String packageName) {
this.mPackageName = packageName;
}
VendorPackageSettings(String packageName, boolean intallStatus) {
this.mPackageName = packageName;
this.mIntallStatus = intallStatus;
}
boolean getIntallStatus() {
return mIntallStatus;
}
void setIntallStatus(boolean mIntallStatus) {
this.mIntallStatus = mIntallStatus;
}
String getPackageName() {
return mPackageName;
}
}
五、xml文件
下面我们来看下这个xml文件
custom-packages.xml
内容如下:
<packages>
<package name="com.iflytek.inputmethod" installStatus="installed" />
<package name="com.fihtdc.note" installStatus="installed" />
<package name="cn.wps.moffice_eng" installStatus="uninstalled" />
</packages>
六、Android.mk文件
我们再来看下Android.mk文件,这里是直接用apk文件编译到system/third_app目录下
include $(CLEAR_VARS)
LOCAL_MODULE := IflytekInput
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_MODULE_PATH := $(TARGET_OUT)/third_app
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := NotePadPlus
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_MODULE_PATH := $(TARGET_OUT)/third_app
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := MOffice
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_MODULE_PATH := $(TARGET_OUT)/third_app
include $(BUILD_PREBUILT)
七、补充 & 改良方案
最后这个方案在android6.0上,还是失败了。原因是apk在system目录下,SystemServer和Installdf有很多权限没有。导致在生成dex2oat和生成lib库时都出错。
最后我们在扫描这个目录的时候添加,系统应用的两个flag。这样就能安装好。
scanDirLI(operatorAppDir, PackageParser.PARSE_IS_VENDOR
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
一开始方案是准备这样的,先按系统应用的方式扫描安装,这样会把oat文件放在data/dalvik-cache, lib库也会在system/lib中。但是扫描最后把系统应用变成普通应用就是flag改掉。
方法是在后面的扫描流程中添加如下代码添加在scanPackageDirtyLI函数中。添加如下代码代表是在system/third_app下面的apk,不是系统应用。但是扫描安装的时候又是按系统应用安装的。
if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {
pkg.applicationInfo.flags &= (~ApplicationInfo.FLAG_SYSTEM);
pkg.coreApp = false;
}
而且一定要在下面函数之前,下面函数是在scanPackageDirtyLI函数中
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
我们来看Settings中insertPackageSettingLPw函数如下代码,把会PackageParser.Package的flags复制到PackageSetting的pkgFlags中。
if (pkg.applicationInfo.flags != p.pkgFlags) {
p.pkgFlags = pkg.applicationInfo.flags;
}
而判断是否是系统应用有两个函数就是根据上面的两个flag,第一个参数PackageParser.Package
private static boolean isSystemApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
第二个参数PackageSetting。
private static boolean isSystemApp(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
当然最后我的方案还是错了,虽然系统启动扫描apk安装成功了,而且因为isSystemApp也生效了,在Launcher中确实能删掉apk。但是最致命的一点就是如果不是系统app的话,当apk中调用库函数,库函数放在system/lib下没有系统apk的权限,直接崩溃。
所以这套方案行不通。
所以修改flag使其变成非系统应用,不行。
剩下还有两个个方案。
第一个就是在卸载app的地方。直接不用是否系统应用来决定删除,我们可以先看flag是否有pkg.applicationInfo.flags |= ApplicationInfo.PRIVATE_FLAG_UNINSTALL_APP如下flag,有也可以直接删。然后在PKMS删的地方再做处理。
另一种方案就是扫描system/third_app不传入system应用的flag。就要硬着头皮,在生成oat和lib的地方更换有权限的目录。当然说是这么简单里面有很多逻辑。
最后我采用了第二种方案,第一种方案因为要修改的地方很多,而且也不是标准的,第二种方法其实有一种简单的方法可以实现不用修改代码,原先system/third_app下每个app有一个目录导致最后所有在生成一些库和oat文件会在这个system目录而导致没有权限,如果没有这个目录,apk文件直接在system/third_app下lib和oat文件就会放在data相关目录下,也就有权限了。这个问题也就解决了。
但是Android6.0编译apk会直接带目录,所以像上面的Android.mk我们不可避免的在system/third_app下面会有目录。
这里我们就在上面方案代码的基础上稍微修改了下,代码如下:
final File operatorAppDir = new File("/system/third_app");
//Add PARSE_IS_VENDOR for operator apps
final File[] operatorAppFiles = operatorAppDir.listFiles();
for (File file : operatorAppFiles) {
Slog.w(TAG, "third_app package file dir: " + file.getName());
scanDirLI(file, PackageParser.PARSE_IS_VENDOR, scanFlags, 0);
}
之前是直接扫描system/third_app,而现在是扫描system/third_app下的每个目录,这样的区别在sacnDirLI中会在遍历每个目录,最后我们在调用sacnPackageLI的时候就是apk文件而不是目录了,这样生成的一些lib和oat文件就不会在system目录下了,自然也不会有权限问题了。
所以看来这一系列的方案中,最后这种修改是最好的。