背景:

最近工作中,有些项目不允许所有APK都拥有安装权限,例如apk只能通过应用商城来安装或者升级,只允许某些特定的apk自升级,不允许pm install等。这就需要添加安装权限白名单来控制。

Android apk安装方式

先介绍Android中常用的几种安装方式,好针对这几种进行修改
1、 直接调用安装接口。

Uri mPackageURI = Uri.fromFile(new File(Environment.getExternalStorageDirectory() + apkName));int installFlags = 0;PackageManager pm = getPackageManager();try{    PackageInfo pi = pm.getPackageInfo(packageName,    PackageManager.GET_UNINSTALLED_PACKAGES);    if(pi != null) {        installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;    }}catch (NameNotFoundException e){}PackageInstallObserver observer = new PackageInstallObserver();pm.installPackage(mPackageURI, observer, installFlags);

这种修改需要直接修改packageManagerService。对应下面的第一种方法。

2、通过Intent机制,调用packageInstaller进行安装。

String fileName = Environment.getExternalStorageDirectory() + apkName;Uri uri = Uri.fromFile(new File(fileName));Intent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(Uri, application/vnd.android.package-archive");startActivity(intent);

因为应用是通过packageInstaller进行安装的,相当于隔了一层代理,所以在packageManagerService并无法判断正在调用安装的是哪个app,只能在packageInstaller中进行修改,参考下面的第二中方法。

3、通过命令进行安装 pm install,参考第三种方法修改。

修改方法:

1、packageManagerService修改

packageManagerService的修改,我们在其中添加几个接口及代码来控制apk安装。

1)增加以下函数:

       /*add for installer white list*/    private boolean isInstallerEnable(String packagename){        ArrayList<String> whiteListApp = new ArrayList<String>();        try{            BufferedReader br = new BufferedReader(new InputStreamReader(            new FileInputStream("/system/etc/WhiteListAppFilter.properties")));            String line ="";            while ((line = br.readLine()) != null){                whiteListApp.add(line);            }            br.close();        }catch(java.io.FileNotFoundException ex){            return false;        }catch(java.io.IOException ex){            return false;        }        Iterator<String> it = whiteListApp.iterator();        while (it.hasNext()) {            String whitelisItem = it.next();            if (whitelisItem.equals(packagename)) {                return true;            }        }        return false;    }

isInstallerEnable函数会去读取白名单文件/system/etc/WhiteListAppFilter.properties,然后和我们传进来的包名进行匹配,在白名单中返回true,其他情况均返回false。

2)获取调用的包名
这个可以在installPackageWithVerificationAndEncryption函数中来获取,

        if (uid == Process.SHELL_UID || uid == 0) {            if (DEBUG_INSTALL) {                Slog.v(TAG, "Install from ADB");            }            filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;        } else {            filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;        }        verificationParams.setInstallerUid(uid);        //add for installer white list        mCallingApp = mContext.getPackageManager().getNameForUid(uid);        Log.d(TAG, "!!!!!!!!!!!!!callingApp = " + mCallingApp);        //end        final Message msg = mHandler.obtainMessage(INIT_COPY);        msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,                verificationParams, encryptionParams, user);        mHandler.sendMessage(msg);

接下来要在installPackageLI函数对调用安装的apk进行匹配,判断是否在白名单中,如果不在的话则提示错误,错误码是-xxx(自己定义),这个错误码会给packageInstaller,packageInstaller中需要做什么,就由自己定义了。

        //add for installer white list        boolean caninstall =false;        if(mCallingApp != null && isInstallerEnable(mCallingApp)){            caninstall = true;        }        if(!caninstall){            Toast.makeText(mContext, R.string.install_error, Toast.LENGTH_LONG).show();            res.returnCode = -xxx;            return;        }        //end        if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);        // Retrieve PackageSettings and parse package        int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY                | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)                | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);    ...省略

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

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

2、packageInstaller的修改

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

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

    protected void onCreate(Bundle icicle) {        super.onCreate(icicle);        // get intent information        final Intent intent = getIntent();        mPackageURI = intent.getData();        mPm = getPackageManager();        final int uid = getOriginatingUid(intent);        String callingApp = mPm.getNameForUid(uid);        final File sourceFile = new File(mPackageURI.getPath());        PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);        mPkgInfo = PackageParser.generatePackageInfo(parsed, null,            PackageManager.GET_PERMISSIONS, 0, 0, null,            new PackageUserState());        // add for installer enable/disable         if (!isInstallerEnable(callingApp)) {            Toast.makeText(this, R.string.install_not_allow, Toast.LENGTH_LONG).show();            this.finish();        }        mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); ...省略

3、pm install的修改

禁止pm install,因为有些APK安装竟然是调用pm install命令去安装的。
修改要在pm.java修改,修改方法和上面基本一致。
可以看到,pm install其实调用的是run再去判断参数。

    public static void main(String[] args) {        new Pm().run(args);    }public void run(String[] args) { ...省略        if ("install".equals(op)) {            runInstall();            return;        } ...省略

那我们要添加的话,先获取app名,再和packageManagerService一样,增加isInstallerEnable去判断是不是要调用runInstall()就OK了。

            String callingApp = "";            try {                callingApp = mPm.getNameForUid(Binder.getCallingUid());            } catch(RemoteException re) {                Log.e("Pm", Log.getStackTraceString(new Throwable()));             }

更多相关文章

  1. Android 通用获取Ip的方法(判断手机是否联网的方法)!!!
  2. Android获取音视频原始流数据方法详解
  3. 【Android】Android插件开发 —— 打开插件的Activity(Hook系统方
  4. Android任务切换方法
  5. 读取指定路径数据库的方法
  6. android获取屏幕宽高的两种方法

随机推荐

  1. android 最全的shape属性
  2. android SQLite的使用
  3. View组件之各xml属性
  4. Android环境搭建
  5. Android(安卓)图片侧滑展示RecyclerView
  6. android的TableLayout布局界面元素填满整
  7. Android系列教程之十:Intents and Intent
  8. 手动更新 Android(安卓)SDK
  9. android 笔记 --- Android应用程序的权限
  10. android字体加粗的方法