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

之前两篇博客分析了三方应用如何安装的问题,但是两个方法都有问题

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目录下了,自然也不会有权限问题了。

所以看来这一系列的方案中,最后这种修改是最好的。


 

 

 

 





  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值