之前两篇博客分析了三方应用如何安装的问题,但是两个方法都有问题
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)
原文地址
http://blog.csdn.net/kc58236582/article/details/53285069