背景:

最近工作中,有些项目不允许所有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. QtAndroid详解(3):startActivity实战Android拍照功能
  2. Android模块化(二)——模块通信和模块间服务调用
  3. Ubuntu 14.04 LTS 安装 文泉驿微米黑 字体到android studio
  4. android学习笔记-1
  5. Android异步处理二:使用AsyncTask异步更新UI界面
  6. Android重要类学习之——Activity
  7. 3G之Android学习第一章节>
  8. Android之NDK开发
  9. android中Webview与javascript的交互(互相调用)

随机推荐

  1. Android AAC框架从入门到精通
  2. android之蓝牙设备的使用01
  3. android 4.0.3 设置时间无法更新到RTC
  4. android:maxLines="1"截取显示不全,用andr
  5. 刚学会百度地图最新版的sdk,总结一个简单
  6. android去除Spinner的分割线
  7. MAC OS 下使用Android(安卓)Studio获取SH
  8. Android(安卓)中TextView 添加超链接
  9. Android 天气预报加widget源码
  10. android内核编译方法 转载