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. int installFlags = 0;
  3. PackageManager pm = getPackageManager();
  4. try{
  5. PackageInfo pi = pm.getPackageInfo(packageName,
  6. PackageManager.GET_UNINSTALLED_PACKAGES);
  7. if(pi != null) {
  8. installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;
  9. }
  10. }
  11. catch (NameNotFoundException e){}
  12. PackageInstallObserver observer = new PackageInstallObserver();
  13. 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卡中只要改成"/sdcard/WhiteListAppFilter.properties")


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


总结:只适合自己开发板,不适合手机的方式。

猜你喜欢

转载自blog.csdn.net/jim19890923/article/details/81059726