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

  1. +    final HashMap<String, PackageParser.Package> mVendorPackages =  
  2. +        new HashMap<String, PackageParser.Package>();  
  3.      final Settings mSettings;  
  4. +    final VendorSettings mVendorSettings;  
  5.      boolean mRestoredSettings;  


一、构造函数

然后在PKMS的构造函数中修改如下代码,新建VendorSettings对象,然后哦扫描system/third_app,并且传入参数PackageParser.PARSE_IS_VENDOR。我们来看下构造函数的部分diff文件

  1.    public class PackageManagerService extends IPackageManager.Stub {  
  2.          mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));  
  3.          mMetrics = new DisplayMetrics();  
  4.          mSettings = new Settings(mPackages);  
  5. +        mVendorSettings = new VendorSettings();  
  6.          mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,  
  7.                  ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);  
  8.          mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,  
  9. @@ -2015,6 +2020,8 @@ public class PackageManagerService extends IPackageManager.Stub {  
  10.                  mCustomResolverComponentName = ComponentName.unflattenFromString(  
  11.                          customResolverActivity);  
  12.              }  
  13. +            //get vendor package info  
  14. +            mVendorSettings.readLPw();//读取xml内容到mVendorSettings中  
  15.    
  16.              long startTime = SystemClock.uptimeMillis();  
  17.    
  18. @@ -2194,6 +2201,11 @@ public class PackageManagerService extends IPackageManager.Stub {  
  19.              scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM  
  20.                      | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);  
  21.    
  22. +            final File operatorAppDir = new File("/system/third_app");  
  23. +  
  24. +            //Add PARSE_IS_VENDOR for operator apps  
  25. +            scanDirLI(operatorAppDir, PackageParser.PARSE_IS_VENDOR, scanFlags, 0);  

PackageParser.PARSE_IS_VENDOR是我们再PackageParser中新建的

  1. public final static int PARSE_IS_VENDOR = 1<<10;  


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

  1. if (!mOnlyCore) {  
  2.     Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();  
  3.     while (psit.hasNext()) {  
  4.         PackageSetting ps = psit.next();  
  5.   
  6.         /* 
  7.          * If this is not a system app, it can't be a 
  8.          * disable system app. 
  9.          */  
  10.         /*if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { 
  11.             continue; 
  12.         }*/  
  13.   
  14.         if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0//系统app、我们的三方app需要删减  
  15.             //Vendor apps are belong to system domain, therefore, need prune.  
  16.             && (ps.pkgFlags & ApplicationInfo.PRIVATE_FLAG_UNINSTALL_APP) == 0) {  
  17.             continue;  
  18.         }  
  19.   
  20.         /* 
  21.          * If the package is scanned, it's not erased. 
  22.          */  
  23.         final PackageParser.Package scannedPkg = mPackages.get(ps.name);  
  24.         if (scannedPkg != null) {  
  25.             /* 
  26.              * If the system app is both scanned and in the 
  27.              * disabled packages list, then it must have been 
  28.              * added via OTA. Remove it from the currently 
  29.              * scanned package so the previously user-installed 
  30.              * application can be scanned. 
  31.              */  
  32.             if (mSettings.isDisabledSystemPackageLPr(ps.name)) {  
  33.                 logCriticalInfo(Log.WARN, "Expecting better updated system app for "  
  34.                         + ps.name + "; removing system app.  Last known codePath="  
  35.                         + ps.codePathString + ", installStatus=" + ps.installStatus  
  36.                         + ", versionCode=" + ps.versionCode + "; scanned versionCode="  
  37.                         + scannedPkg.mVersionCode);  
  38.                 removePackageLI(ps, true);  
  39.                 mExpectingBetter.put(ps.name, ps.codePath);  
  40.             }  
  41.   
  42.             continue;  
  43.         }  
  44.   
  45.         if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {  
  46.             psit.remove();  
  47.             logCriticalInfo(Log.WARN, "System package " + ps.name  
  48.                     + " no longer exists; wiping its data");  
  49.             removeDataDirsLI(null, ps.name);  
  50.         } else {  
  51.             final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);  
  52.             if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {  
  53.                 possiblyDeletedUpdatedSystemApps.add(ps.name);  
  54.             }  
  55.         }  
  56.     }  
  57. }  
  58. Iterator<VendorPackageSettings> vpsit = mVendorSettings.mVendorPackages.values().iterator();  
  59. while (vpsit.hasNext()) {//遍历mVendorSettings  
  60.     VendorPackageSettings vps = vpsit.next();  
  61.     final PackageParser.Package scannedVendorPkg = mVendorPackages.get(vps.getPackageName());  
  62.     if (scannedVendorPkg == null) {  
  63.         vpsit.remove();  
  64.         Slog.w(TAG, "Vendor package: " + vps.getPackageName()  
  65.             + " has been removed from system");  
  66.     }  
  67. }  

扫描完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文件。

  1. mSettings.writeLPr();  
  2. 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,结束安装。

  1. final PackageParser.Package pkg;  
  2. try {  
  3.     pkg = pp.parsePackage(scanFile, parseFlags);  
  4. catch (PackageParserException e) {  
  5.     throw PackageManagerException.from(e);  
  6. }  
  7.   
  8. if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {  
  9.     if (mVendorPackages.get(pkg.packageName) == null) {  
  10.         mVendorPackages.put(pkg.packageName, pkg);//放入mVendorPackages  
  11.     }  
  12. }  
  13.   
  14. //Check whether we should skip the scan of current package  
  15. //We should only check vendor packages  
  16. if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {  
  17.     VendorPackageSettings vps = mVendorSettings.mVendorPackages.get(pkg.packageName);//扫描mVendorSettings的pkg  
  18.     if (vps != null) {  
  19.         if (!vps.getIntallStatus()) {  
  20.             //Skip the vendor package that was uninstalled by user  
  21.             Log.i(TAG, "Package "  + vps.getPackageName()+ " skipped due to uninstalled");  
  22.             return null;  
  23.         }  
  24.     }  
  25. }  

扫描目录安装最后会调用scanPackageDirtyLI函数,我们在这个函数一开始当有PackageParser.PARSE_IS_VENDOR这个flag,其pkg的applicationInfo.flags |= ApplicationInfo.PRIVATE_FLAG_UNINSTALL_APP

  1. if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {  
  2.     pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;  
  3. }  
  4. if ((parseFlags&PackageParser.PARSE_IS_VENDOR) != 0) {  
  5.     pkg.applicationInfo.flags |= ApplicationInfo.PRIVATE_FLAG_UNINSTALL_APP;  
  6. }  

继续这个函数,在writer部分,当有PackageParser.PARSE_IS_VENDOR的flag,我们把这个pkg放入mVendorSettings中,并且其状态为true。最后扫描完会更新其xml文件。

  1. // writer  
  2. synchronized (mPackages) {  
  3.     // We don't expect installation to fail beyond this point  
  4.   
  5.     // Add the new setting to mSettings  
  6.     mSettings.insertPackageSettingLPw(pkgSetting, pkg);  
  7.     // Add the new setting to mPackages  
  8.     mPackages.put(pkg.applicationInfo.packageName, pkg);  
  9.     // Make sure we don't accidentally delete its data.  
  10.     final Iterator<PackageCleanItem> iter = mSettings.mPackagesToBeCleaned.iterator();  
  11.     while (iter.hasNext()) {  
  12.         PackageCleanItem item = iter.next();  
  13.         if (pkgName.equals(item.packageName)) {  
  14.             iter.remove();  
  15.         }  
  16.     }  
  17.   
  18.     //If the newly installed package is vendor app,  
  19.     //add or update it in vendor settings  
  20.     if ((parseFlags & PackageParser.PARSE_IS_VENDOR) != 0) {  
  21.         mVendorSettings.insertPackage(pkg.packageName,true);  
  22.     }  



三、删除应用

最后我们再看下删除应用的情况,删除应用最后会调用removePackageDataLI函数,我们来看下这个函数的diff文件

  1.      public class PackageManagerService extends IPackageManager.Stub {  
  2.          removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);  
  3.          // Retrieve object to delete permissions for shared user later on  
  4.          final PackageSetting deletedPs;  
  5. +        final VendorPackageSettings delVps;  
  6.          // reader  
  7.          synchronized (mPackages) {  
  8.              deletedPs = mSettings.mPackages.get(packageName);  
  9. +            delVps = mVendorSettings.mVendorPackages.get(packageName);//需要在mVendorSettings中删除的应用  
  10.              if (outInfo != null) {  
  11.                  outInfo.removedPackage = packageName;  
  12.                  outInfo.removedUsers = deletedPs != null  
  13. @@ -13268,6 +13326,12 @@ public class PackageManagerService extends IPackageManager.Stub {  
  14.              // from KeyStore.  
  15.              removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);  
  16.          }  
  17. +               if (delVps != null) {  
  18. +            //If the deleted package is vendor package  
  19. +            //remove it from vendor settins  
  20. +            mVendorSettings.setPackageStatus(packageName, false);//设置其删除应用状态为uninstalled  
  21. +            mVendorSettings.writeLPr();//更新到xml中  
  22. +        }  
  23.      }  


四、VendorSettings类

下面我们再来看下VendorSettings 类是模仿PKMS的Settings类,更新xml文件的。

  1. /* 
  2.  * Copyright (C) 2008 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.server.pm;  
  18.   
  19. import java.io.BufferedOutputStream;  
  20. import java.io.File;  
  21. import java.io.FileInputStream;  
  22. import java.io.FileOutputStream;  
  23. import java.io.IOException;  
  24. import java.util.HashMap;  
  25.   
  26. import javax.xml.parsers.DocumentBuilder;  
  27. import javax.xml.parsers.DocumentBuilderFactory;  
  28. import javax.xml.parsers.ParserConfigurationException;  
  29.   
  30.   
  31. import org.w3c.dom.Document;  
  32. import org.w3c.dom.Element;  
  33. import org.w3c.dom.NamedNodeMap;  
  34. import org.w3c.dom.Node;  
  35. import org.w3c.dom.NodeList;  
  36. import org.xml.sax.SAXException;  
  37. import org.xmlpull.v1.XmlPullParser;  
  38. import org.xmlpull.v1.XmlPullParserException;  
  39. import org.xmlpull.v1.XmlSerializer;  
  40.   
  41. import com.android.internal.util.FastXmlSerializer;  
  42.   
  43.   
  44. import android.os.Environment;  
  45. import android.os.FileUtils;  
  46. import android.util.Log;  
  47. import android.util.Slog;  
  48. import android.util.Xml;  
  49.   
  50. final class VendorSettings {  
  51.   
  52.     private static final String TAG_ROOT = "packages";  
  53.     private static final String TAG_PACKAGE = "package";  
  54.     static final String ATTR_PACKAGE_NAME = "name";  
  55.     static final String ATTR_INSTALL_STATUS = "installStatus";  
  56.     static final String VAL_INSTALLED = "installed";  
  57.     static final String VAL_UNINSTALLED = "uninstalled";  
  58.   
  59.     private final File mSystemDir;  
  60.     private final File mVendorSettingsFilename;  
  61.     private final File mVendorBackupSettingsFilename;  
  62.   
  63.     final HashMap<String, VendorPackageSettings> mVendorPackages =  
  64.             new HashMap<String, VendorPackageSettings>();  
  65.     VendorSettings() {  
  66.         this(Environment.getDataDirectory());  
  67.     }  
  68.     VendorSettings(File dataDir) {  
  69.         mSystemDir = new File(dataDir, "system");;  
  70.         mSystemDir.mkdirs();  
  71.         FileUtils.setPermissions(mSystemDir.toString(),  
  72.                 FileUtils.S_IRWXU|FileUtils.S_IRWXG  
  73.                 |FileUtils.S_IROTH|FileUtils.S_IXOTH,  
  74.                 -1, -1);  
  75.         mVendorSettingsFilename = new File(mSystemDir, "custom-packages.xml");//xml文件  
  76.         mVendorBackupSettingsFilename = new File(mSystemDir, "custom-packages-backup.xml");//xml备份文件  
  77.     }  
  78.   
  79.     void insertPackage(String packageName, boolean installStatus) {  
  80.         VendorPackageSettings vps = mVendorPackages.get(packageName);  
  81.         if (vps != null) {  
  82.             vps.setIntallStatus(installStatus);  
  83.         } else {  
  84.             vps = new VendorPackageSettings(packageName, installStatus);  
  85.             mVendorPackages.put(packageName, vps);  
  86.         }  
  87.     }  
  88.   
  89.     void setPackageStatus(String packageName, boolean installStatus) {  
  90.         VendorPackageSettings vps = mVendorPackages.get(packageName);  
  91.         if (vps == null) {  
  92.             //Shall we return a much meaningful result?  
  93.             return;  
  94.         } else {  
  95.             vps.setIntallStatus(installStatus);  
  96.         }  
  97.     }  
  98.   
  99.     void removePackage(String packageName) {  
  100.         if (mVendorPackages.get(packageName) != null) {  
  101.             mVendorPackages.remove(packageName);  
  102.         }  
  103.     }  
  104.   
  105.     void readLPw() {  
  106.         FileInputStream str = null;  
  107.         DocumentBuilderFactory docBuilderFactory = null;  
  108.         DocumentBuilder docBuilder = null;  
  109.         Document doc = null;  
  110.         if (mVendorBackupSettingsFilename.exists()) {//先看备份是否有备份文件,有代表之前写的时候失败了,就要使用备份文件  
  111.             try {  
  112.                 str = new FileInputStream(mVendorBackupSettingsFilename);  
  113.                 if (mVendorSettingsFilename.exists()) {  
  114.                     //If both the backup and vendor settings file exist, we  
  115.                     //ignore the settings since it might have been corrupted.  
  116.                     Slog.w(PackageManagerService.TAG, "Cleaning up settings file");  
  117.                     mVendorSettingsFilename.delete();  
  118.                 }  
  119.             } catch (java.io.IOException e) {  
  120.   
  121.             }  
  122.         }  
  123.   
  124.         try {  
  125.             if (str == null) {  
  126.                 if (!mVendorSettingsFilename.exists()) {  
  127.                     return;  
  128.                 }  
  129.                 str = new FileInputStream(mVendorSettingsFilename);  
  130.             }  
  131.             docBuilderFactory = DocumentBuilderFactory.newInstance();  
  132.             docBuilder = docBuilderFactory.newDocumentBuilder();  
  133.             doc = docBuilder.parse(str);  
  134.             Element root = doc.getDocumentElement();  
  135.             NodeList nodeList = root.getElementsByTagName(TAG_PACKAGE);  
  136.             Node node = null;  
  137.             NamedNodeMap nodeMap = null;  
  138.             String packageName = null;  
  139.             String installStatus = null;  
  140.             for (int i = 0; i < nodeList.getLength(); i++) {//读取xml文件内容到mVendorPackages中  
  141.                 node = nodeList.item(i);  
  142.                 if (node.getNodeName().equals(TAG_PACKAGE)) {  
  143.                     nodeMap = node.getAttributes();  
  144.                     packageName = nodeMap.getNamedItem(ATTR_PACKAGE_NAME).getTextContent();  
  145.                     installStatus = nodeMap.getNamedItem(ATTR_INSTALL_STATUS).getTextContent();  
  146.                     mVendorPackages.put(packageName,  
  147.                             new VendorPackageSettings(packageName, installStatus.equals(VAL_INSTALLED)));  
  148.                 }  
  149.             }  
  150.         } catch (java.io.IOException e) {  
  151.             e.printStackTrace();  
  152.         } catch (ParserConfigurationException e) {  
  153.             e.printStackTrace();  
  154.         } catch (SAXException e) {  
  155.             e.printStackTrace();  
  156.         }  
  157.     }  
  158.   
  159.     void writeLPr() {  
  160.         if (mVendorSettingsFilename.exists()) {  
  161.             if (!mVendorBackupSettingsFilename.exists()) {//创建备份文件类似mSettings的处理方法  
  162.                 if (!mVendorSettingsFilename.renameTo(mVendorBackupSettingsFilename)) {  
  163.                     Slog.e(PackageManagerService.TAG, "Unable to backup package manager vendor settings, "  
  164.                             + " current changes will be lost at reboot");  
  165.                     return;  
  166.                 }  
  167.             } else {  
  168.                 mVendorSettingsFilename.delete();  
  169.                 Slog.w(PackageManagerService.TAG, "Preserving older vendor settings backup");  
  170.             }  
  171.         }  
  172.         try {  
  173.             FileOutputStream fstr = new FileOutputStream(mVendorSettingsFilename);  
  174.             XmlSerializer serializer = new FastXmlSerializer();  
  175.             //XmlSerializer serializer = Xml.newSerializer()  
  176.             BufferedOutputStream str = new BufferedOutputStream(fstr);  
  177.             serializer.setOutput(str, "utf-8");  
  178.             serializer.startDocument(null, true);  
  179.             serializer.startTag(null, TAG_ROOT);  
  180.   
  181.             for (VendorPackageSettings ps : mVendorPackages.values()) {//将mVendorPackages中的状态写入xml文件  
  182.                 serializer.startTag(null, TAG_PACKAGE);  
  183.                 serializer.attribute(null, ATTR_PACKAGE_NAME, ps.getPackageName());  
  184.                 serializer.attribute(null, ATTR_INSTALL_STATUS,  
  185.                         ps.getIntallStatus() ? VAL_INSTALLED : VAL_UNINSTALLED);  
  186.                 serializer.endTag(null, TAG_PACKAGE);  
  187.             }  
  188.             serializer.endTag(null, TAG_ROOT);  
  189.             serializer.endDocument();  
  190.             str.flush();  
  191.             FileUtils.sync(fstr);  
  192.             str.close();  
  193.   
  194.             mVendorBackupSettingsFilename.delete();  
  195.             FileUtils.setPermissions(mVendorSettingsFilename.toString(),  
  196.                     FileUtils.S_IRUSR|FileUtils.S_IWUSR  
  197.                     |FileUtils.S_IRGRP|FileUtils.S_IWGRP,  
  198.                     -1, -1);  
  199.         } catch (IllegalArgumentException e) {  
  200.             e.printStackTrace();  
  201.         } catch (IllegalStateException e) {  
  202.             e.printStackTrace();  
  203.         } catch (IOException e) {  
  204.             e.printStackTrace();  
  205.         }  
  206.     }  
  207. }  

VendorPackageSettings 是描述每个pkg安装的状态。

  1. /* 
  2.  * Copyright (C) 2008 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.server.pm;  
  18.   
  19. final class VendorPackageSettings {  
  20.   
  21.     final String mPackageName;  
  22.     boolean mIntallStatus = true;  
  23.   
  24.     VendorPackageSettings(String packageName) {  
  25.         this.mPackageName = packageName;  
  26.     }  
  27.   
  28.     VendorPackageSettings(String packageName, boolean intallStatus) {  
  29.         this.mPackageName = packageName;  
  30.         this.mIntallStatus = intallStatus;  
  31.     }  
  32.   
  33.     boolean getIntallStatus() {  
  34.         return mIntallStatus;  
  35.     }  
  36.   
  37.     void setIntallStatus(boolean mIntallStatus) {  
  38.         this.mIntallStatus = mIntallStatus;  
  39.     }  
  40.   
  41.     String getPackageName() {  
  42.         return mPackageName;  
  43.     }  
  44. }  


五、xml文件

下面我们来看下这个xml文件

  1. custom-packages.xml  
内容如下:
  1. <packages>  
  2. <package name="com.iflytek.inputmethod" installStatus="installed" />  
  3. <package name="com.fihtdc.note" installStatus="installed" />  
  4. <package name="cn.wps.moffice_eng" installStatus="uninstalled" />  
  5. </packages>  


六、Android.mk文件

我们再来看下Android.mk文件,这里是直接用apk文件编译到system/third_app目录下

  1. include $(CLEAR_VARS)  
  2. LOCAL_MODULE := IflytekInput  
  3. LOCAL_SRC_FILES := $(LOCAL_MODULE).apk  
  4. LOCAL_MODULE_TAGS := optional  
  5. LOCAL_MODULE_CLASS := APPS  
  6. LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)  
  7. LOCAL_CERTIFICATE := PRESIGNED  
  8. LOCAL_DEX_PREOPT := false  
  9. LOCAL_MODULE_PATH := $(TARGET_OUT)/third_app  
  10. include $(BUILD_PREBUILT)  
  11.   
  12. include $(CLEAR_VARS)  
  13. LOCAL_MODULE := NotePadPlus  
  14. LOCAL_SRC_FILES := $(LOCAL_MODULE).apk  
  15. LOCAL_MODULE_TAGS := optional  
  16. LOCAL_MODULE_CLASS := APPS  
  17. LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)  
  18. LOCAL_CERTIFICATE := PRESIGNED  
  19. LOCAL_DEX_PREOPT := false  
  20. LOCAL_MODULE_PATH := $(TARGET_OUT)/third_app  
  21. include $(BUILD_PREBUILT)  
  22.   
  23. include $(CLEAR_VARS)  
  24. LOCAL_MODULE := MOffice  
  25. LOCAL_SRC_FILES := $(LOCAL_MODULE).apk  
  26. LOCAL_MODULE_TAGS := optional  
  27. LOCAL_MODULE_CLASS := APPS  
  28. LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)  
  29. LOCAL_CERTIFICATE := PRESIGNED  
  30. LOCAL_DEX_PREOPT := false  
  31. LOCAL_MODULE_PATH := $(TARGET_OUT)/third_app  
  32. include $(BUILD_PREBUILT) 

原文地址

http://blog.csdn.net/kc58236582/article/details/53285069



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值