您的位置:首页 > 移动开发

Android6.0关于预置三方app卸载 扫描system/third_app目录

2016-11-24 10:45 549 查看
之前两篇博客分析了三方应用如何安装的问题,但是两个方法都有问题

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文件的。

[cpp]
view plain
copy





+    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文件

[cpp]
view plain
copy





   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中新建的

[cpp]
view plain
copy





public final static int PARSE_IS_VENDOR = 1<<10;  

我们再来看构造函数下面这段代码,系统应用在扫描完目录之后,需要对mSettings的mPackages进行删减,而system/third_app也属于这个范畴。

[cpp]
view plain
copy





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文件。

[cpp]
view plain
copy





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,结束安装。

[cpp]
view plain
copy





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

[cpp]
view plain
copy





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文件。

[cpp]
view plain
copy





// 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文件

[cpp]
view plain
copy





     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文件的。

[cpp]
view plain
copy





/* 
 * 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安装的状态。

[cpp]
view plain
copy





/* 
 * 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文件

[cpp]
view plain
copy





custom-packages.xml  

内容如下:

[html]
view plain
copy





<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目录下

[cpp]
view plain
copy





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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: