Android 5.1.1源码修改添加白名单和静默安装功能

最近弄项目都是些要改源码才能实现的,像静默安装和白名单功能.

 

静默安装:

       1:在源码的AndroidMainfest.xml中添加权限

      --- a/frameworks/base/core/res/AndroidManifest.xml
+++ b/frameworks/base/core/res/AndroidManifest.xml
@@ -2451,6 +2451,10 @@
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.INSTALL_PACKAGES"
         android:protectionLevel="signature|privileged" />
+    <permission android:name="android.permission.HIDE_INSTALL_PACKAGES"
+        android:protectionLevel="normal" />
+    <permission android:name="android.permission.HIDE_UNINSTALL_PACKAGES"
+        android:protectionLevel="normal" />

     2 :在IPackManger.Stub中添加权限判断

--- a/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11704,7 +11704,13 @@ public class PackageManagerService extends IPackageManager.Stub {
     public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
             int installFlags, String installerPackageName, int userId) {
         android.util.SeempLog.record(90);
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+        //mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+        if(mContext.checkCallingPermission(android.Manifest.permission.HIDE_INSTALL_PACKAGES) == PackageManager.PERMISSION_GRANTED) {
+            Slog.i(TAG, "installerPackageName: checkCallingPermission "+installerPackageName);
+        } else {
+            Slog.i(TAG, "installerPackageName: checkCallingPermission PERMISSION_DENIED"+PackageManager.PERMISSION_DENIED);
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+        }

 

3:在APP的libs添加pminstall.jar

4:在APP中添加<uses-permission android:name="android.permission.HIDE_INSTALL_PACKAGES" />权限

静默安装就成功了!!!撒花庆祝。

demo的连接地址:https://github.com/ChloeDimen/pmApplication

白名单功能:因为也是在在源码中修改,查了很多资料和自己的理解,有可能有问题。希望大家理解,有问题大家在评论中提出。

      本人希望白名单是可以在SD卡中修改的,查资料是把白名单打包到系统的,所有自己修改验证。

      先说下android的apk安装方式,在修改源码。

   1:直接调用安装接口

 

  1. Uri mPackageURI = Uri.fromFile(new File(Environment.getExternalStorageDirectory() + apkName));

  2.  
  3. int installFlags = 0;

  4. PackageManager pm = getPackageManager();

  5. try{

  6. PackageInfo pi = pm.getPackageInfo(packageName,

  7. PackageManager.GET_UNINSTALLED_PACKAGES);

  8. if(pi != null) {

  9. installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;

  10. }

  11. }

  12. catch (NameNotFoundException e){}

  13. PackageInstallObserver observer = new PackageInstallObserver();

  14. pm.installPackage(mPackageURI, observer, installFlags);

 

这种方式一般在APP中,我们是用AIDl的集成,在调用pm.installpackage(mPackageURI, observer, installFlags)。不过要配置很多东西,AIDl导入也要很多错误,要自己修改。app还需要在AndroidManifest增加一句 android:sharedUserId=”android.uid.system”,而且还要用系统的签名工具。因为每个版本和每种不同手机的签名工具不一样,所有在手机上不适合。在市面上像完成静默安装,我知道的只有开发应用市场类的APP。

 

   我现在可以修改源码,就是在自己的板子上开发了!!!

 

2:通过intent机制,调用packageInstall进行安装(有弹框)

 

  1. String fileName = Environment.getExternalStorageDirectory() + apkName;

  2. Uri uri = Uri.fromFile(new File(fileName));

  3. Intent intent = new Intent(Intent.ACTION_VIEW);

  4. intent.setDataAndType(Uri, application/vnd.android.package-archive");

  5. startActivity(intent)

 

3:通过命令进行安装 pm install   

 

修改源码:

1:packageMangerServer修改

源码位置:frameworks\base\services\core\java\com\android\server\pm\PackageMangerServer.java,对源码不熟悉找位置找了好久(后面的现在方便了)。

1)添加函数判断

 /*add for installer white list*/
    private boolean isInstallerEnable(String packagename){
        ArrayList<String> whiteListApp = new ArrayList<String>();
         Log.e(TAG, "mCallingApp:==5 " );
        try{
            BufferedReader br = new BufferedReader(new InputStreamReader(
            new FileInputStream("/sdcard/WhiteListAppFilter.properties")));
                 Log.e(TAG, "mCallingApp:==6 " );
            String line ="";
            while ((line = br.readLine()) != null){
 Log.e(TAG, "mCallingApp:==7 " );
                whiteListApp.add(line);
 Log.e(TAG, "mCallingApp:==8 " );
            }


            br.close();
        }catch(java.io.FileNotFoundException ex){
            return false;
        }catch(java.io.IOException ex){
            return false;
        }
          Log.e(TAG, "mCallingApp:==9 " );
        Iterator<String> it = whiteListApp.iterator();
          Log.e(TAG, "mCallingApp:==10 " );
        while (it.hasNext()) {
 Log.e(TAG, "mCallingApp:==11 " );
            String whitelisItem = it.next();
Log.e(TAG, "mCallingApp:==12 "+" ,whitelisItem"+whitelisItem+" ,packagename"+packagename );
            if (whitelisItem.equals(packagename)) {
 Log.e(TAG, "mCallingApp:==13 " );
                return true;
            }
        }
 Log.e(TAG, "mCallingApp:==14 " );
        return false;
    }

isWhiteListApp函数会去读取白名单文件/system/etc/whitelistapps,然后和我们传进来的包名进行匹配,在白名单中返回true,其他情况均返回false。这是在白名单不可以给别人修改的,我要在SD卡中"/sdcard/WhiteListAppFilter.properties"),自己修改,下面就统一这个地址。日志就不删除了,偷懒中。

重要的:要导包,查资料又是没有,苦逼。好的自己加

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import java.util.Iterator;

2)获取调用的包名判断是否在白名单中

接下来要在installPackageLI函数对调用安装的apk进行匹配,判断是否在白名单中,如果不在的话则提示错误。注释start和end的代码。

 private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        final int installFlags = args.installFlags;
        String installerPackageName = args.installerPackageName;
        File tmpPackageFile = new File(args.getCodePath());
        boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
        boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);
        boolean replace = false;
        final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
        // Result object to be returned
        res.returnCode = PackageManager.INSTALL_SUCCEEDED;

 。。。。。。。。

 try {
            pp.collectCertificates(pkg, parseFlags);
            pp.collectManifestDigest(pkg);
        } catch (PackageParserException e) {
            res.setError("Failed collect during installPackageLI", e);
            return;
        }

  //  start
        if(!isInstallerEnable(pkg.packageName)) {
            Log.e(TAG, "mCallingApp:==1 "+pkg.packageName);
            res.setError(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                    "app is not in the whitelist. packageName:" + pkg.packageName);
Log.e(TAG, "mCallingApp:==2 " );
            return;
        }
        //  end


        /* If the installer passed in a manifest digest, compare it now. */
        if (args.manifestDigest != null) {
            if (DEBUG_INSTALL) {
                final String parsedManifest = pkg.manifestDigest == null ? "null"
                        : pkg.manifestDigest.toString();
                Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
                        + parsedManifest);
            }

3)添加白名单

 

/system/etc/whitelistapps内容如下,在编译时可以在mk中修改拷贝到etc目录下,例如下面就是允许这三个包名有安装权限。

com.xxx.xxx1 
com.xxx.xxx2 
com.xxx.xxx3

这是在系统的可以,但在在SD卡中汇报没有权限的错误。

 

2、packageInstall的修改

 

还是参考packageManagerService的修改,增加isInstallerEnable函数,去读取白名单文件/"/sdcard/WhiteListAppFilter.properties"),然后进行包名匹配,在白名单中返回true,其他情况均返回false。

我们在packageInstaller的PackageInstallerActivity.java中增加以下修改// add for installer enable/disable ,不在白名单中的app,会直接提示不允许安装后退出。

@Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);


        mPm = getPackageManager();
        mInstaller = mPm.getPackageInstaller();
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);


        final Intent intent = getIntent();
        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
                finish();
                return;
            }

。。。。。。。。

 

//set view
        setContentView(R.layout.install_start);
        mInstallConfirm = findViewById(R.id.install_confirm_panel);
        mInstallConfirm.setVisibility(View.INVISIBLE);
        PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);


        mOriginatingUid = getOriginatingUid(intent);


        // add for installer enable/disable 
        if (!isInstallerEnable(mPkgInfo.packageName)) {
  Log.e(TAG, "mCallingApp:==1 " );
            //Toast.makeText(this, R.string.install_not_allow, Toast.LENGTH_LONG).show();
             this.finish();
  Log.e(TAG, "mCallingApp:==2 " );
        }


        // Block the install attempt on the Unknown Sources setting if necessary.
        if (!requestFromUnknownSource) {
            initiateInstall();
            return;
        }


        // If the admin prohibits it, or we're running in a managed profile, just show error
        // and exit. Otherwise show an option to take the user to Settings to change the setting.
        final boolean isManagedProfile = mUserManager.isManagedProfile();
        if (!unknownSourcesAllowedByAdmin
                || (!unknownSourcesAllowedByUser && isManagedProfile)) {
            showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
        } else if (!unknownSourcesAllowedByUser) {
            // Ask user to enable setting first
            showDialogInner(DLG_UNKNOWN_SOURCES);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
        } else {
            initiateInstall();
        }
    }


    /** Get the ApplicationInfo for the calling package, if available */
    private ApplicationInfo getSourceInfo() {
        String callingPackage = getCallingPackage();
        if (callingPackage != null) {
            try {
                return mPm.getApplicationInfo(callingPackage, 0);
            } catch (NameNotFoundException ex) {
                // ignore
            }
        }
        return null;
    }

3、pm install 的修改

禁止pm install,因为有些APK安装竟然是调用pm install命令去安装的。 
修改要在pm.java修改,修改方法和上面基本一致。 

可以看到,pm install其实调用的是run再去判断参数。

 

最后还是调用

 mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,

                    installerPackageName, verificationParams, abi, userId);

 

应该修改了packagemangerServer.java 就可以了。

 

4、在网上还找到一种方式(有带验证),不过感觉还跟简单

 

1.定义一些全局变量,文件位置:

Build.java (frameworks\base\core\java\android\os) 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/**

 * 包管理方式名称<br>

 *     whitelist: 白名单方式

 *     certificate: 证书认证方式

 *     none: 不进行管理

 */

public static String packageManage = "none";

/**

 * 允许 Launch 显示的 APP 及 APP 白名单

 */

public static String[] packageAllow = new String[]{ "com.baidu.searchbox",

                            "com.thinta.product.thintazlib",

                            "com.thinta.product.x4usertool"};

/**

 * 允许 Launch 显示的 APP的 证书存放路径

 */

public static String certificatePath = "/system/etc/security/media.zip";

 

2.修改安装APK过程,在安装过程添加验证

修改文件的位置:

PackageManagerService.java (frameworks\base\services\core\java\com\android\server\pm) 

首先添加一个函数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

private static HashSet<X509Certificate> getTrustedCerts(File keystore)

        throws IOException, GeneralSecurityException {

        HashSet<X509Certificate> trusted = new HashSet<X509Certificate>();

        if (keystore == null) {

            return trusted;

        }

        ZipFile zip = new ZipFile(keystore);

        try {

            CertificateFactory cf = CertificateFactory.getInstance("X.509");

            Enumeration<? extends ZipEntry> entries = zip.entries();

            while (entries.hasMoreElements()) {

                ZipEntry entry = entries.nextElement();

                InputStream is = zip.getInputStream(entry);

                try {

                    trusted.add((X509Certificate) cf.generateCertificate(is));

                finally {

                    is.close();

                }

            }

        finally {

            zip.close();

        }

        return trusted;

    }

修改的函数:private void installPackageLI(InstallArgs args, PackageInstalledInfo res) 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

第一处修改:

     if(Build.ThintaCust.packageManage.equals("certificate"))

            tmp_flags = PackageManager.GET_SIGNATURES;

        final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY

                | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)

                | (onSd ? PackageParser.PARSE_ON_SDCARD : 0) | tmp_flags;

 

第二处修改:

        if(Build.ThintaCust.packageManage.equals("none")){

            Log.d("XYP_DEBUG""packageManage = none  \n");

        }else if(Build.ThintaCust.packageManage.equals("whitelist")){

            Log.d("XYP_DEBUG""packageManage = whitelist  \n");

            List<String> list = Arrays.asList(Build.ThintaCust.packageAllow);

            if(list.contains(pkg.packageName)){

                Log.d("XYP_DEBUG""can install \n");

            }else{

                Log.d("XYP_DEBUG""forbid install \n");

                res.setError(PackageManager.INSTALL_FAILED_USER_RESTRICTED, "installPackageLI, forbid install");

                return;

            }

        }else if(Build.ThintaCust.packageManage.equals("certificate")){

            int verify_pass = 0;

            try{

                File file = new File(Build.ThintaCust.certificatePath);

                HashSet<X509Certificate> trusted = getTrustedCerts(file);

                CertificateFactory cf = CertificateFactory.getInstance("X.509");

 

                for (X509Certificate c : trusted) {

                    String tmp_public_key = c.getPublicKey().toString();

                    for(Signature sig : pkg.mSignatures)

                    {

                        X509Certificate cert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(sig.toByteArray()));

                        String tmp_key = cert.getPublicKey().toString();

                        if(tmp_public_key.equals(tmp_key)){

                            verify_pass = 1;

                            break;

                        }

                    }

                    if(verify_pass == 1)

                        break;

                }

                if(verify_pass != 1){

                    Log.d("XYP_DEBUG""forbid install \n");

                    res.setError(PackageManager.INSTALL_FAILED_USER_RESTRICTED, "installPackageLI, forbid install");

                    return;

                }

            }catch(FileNotFoundException e){

                Log.d("XYP_DEBUG", e.toString());

            }catch(CertificateException e){

                Log.d("XYP_DEBUG", e.toString());

            }catch(IOException e){

                Log.d("XYP_DEBUG", e.toString());

            }catch(GeneralSecurityException e){

                Log.d("XYP_DEBUG", e.toString());

            }

        }

3.证书的压缩方式:

zip -r media.zip media.x509.pem

直接用命令把*.x509.pem 打包成zip文件,然后放到目标板的合适位置;

用第一步中的certificatePath指向存放该zip文件的位置。 

 

总结:只适合自己开发板,正式版本可能有问题(比如在PackageMangerserver把白名单放在SD卡中,没有读写权限)不适合手机的方式。后续看可以解决吗??

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值