AndroidQ(10.0) 增加包名安装、卸载白名单

简介: AndroidQ(10.0) 增加包名安装、卸载白名单

实现思路


1、提供 ContentProvider 存储 app 包名


2、找到系统安装、卸载 apk 核心代码,查询 app 包名列表,实施拦截


安装卸载的核心代码都在 PackageManagerService.java 中


其中手动点击 apk 调用安装代码在 PackageInstaller 中


文件清单


frameworks\base\packages\PackageInstaller\AndroidManifest.xml
frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\wear\AppPkgListProvider.java
frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\wear\DBHelper.java
frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\PackageInstallerActivity.java
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java

修改方案


1、在 PackageInstaller 中增加 ContentProvider


为啥要选在 PackageInstaller 中,因为这个里面包含手动点击 apk 安装对应界面,其实你要放在其它系统


app 也行

frameworks\base\packages\PackageInstaller\AndroidManifest.xml

   <provider  android:name=".wear.AppPkgListProvider"
            android:authorities="com.android.packageinstaller.app.provider"
            android:exported="true" />


frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\wear\AppPkgListProvider.java

package com.android.packageinstaller.wear;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class AppPkgListProvider extends ContentProvider {
    static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
  static String authorities = "com.android.packageinstaller.app.provider";
    static{  
        matcher.addURI(authorities, "install_app_whitelist", 0);  
        matcher.addURI(authorities, "uninstall_app_blacklist", 1);  
     }  
    DBHelper mDBHelper;
    SQLiteDatabase db;
    @Override
    public boolean onCreate() {
        mDBHelper = new DBHelper(getContext());
        return true;
    }
    @Override
    public Cursor query( Uri uri,  String[] projection,  String selection,  String[] selectionArgs,  String sortOrder) {
        String tableName=getTableName(uri);
        db = mDBHelper.getReadableDatabase();
        return db.query(tableName,
                projection,
                selection,
                selectionArgs,
                null,
                null,
                sortOrder);
    }
    @Override
    public String getType( Uri uri) {
        return null;
    }
    @Override
    public Uri insert( Uri uri,  ContentValues values) {
        String tableName=getTableName(uri);
        db = mDBHelper.getWritableDatabase();
        db.insertWithOnConflict(tableName, null, values,SQLiteDatabase.CONFLICT_REPLACE);
        db.close();
        return null;
    }
    @Override
    public int delete( Uri uri,  String selection,  String[] selectionArgs) {
        String tableName=getTableName(uri);
        db = mDBHelper.getWritableDatabase();
        int row = db.delete(tableName, selection, selectionArgs);
        db.close();
        return row;
    }
    @Override
    public int update( Uri uri,  ContentValues values,  String selection,  String[] selectionArgs) {
        return 0;
    }
    private String getTableName(Uri uri){
        String tableName="installapp_whitelist";
        int match = matcher.match(uri);  
        switch(match){
            case 0:
                tableName="installapp_whitelist";
                break;
            case 1:
                tableName="uninstallapp_blacklist";
                break;
            default:
                break;
        }
        return tableName;
    }
}

frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\wear\DBHelper.java


package com.android.packageinstaller.wear;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBHelper extends SQLiteOpenHelper {
    public DBHelper(Context context){
        super(context, "apppkglist.db", null, 1);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS installapp_whitelist  ( pkg_name TEXT primary key not null,app_name TEXT  )"
        );
        db.execSQL("CREATE TABLE IF NOT EXISTS uninstallapp_blacklist  ( pkg_name TEXT primary key not null,app_name TEXT  )"
        );
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

2、在 PackageInstallerActivity 中查询包名列表进行拦截


在包名清单中查询不到,则提示安装失败,不显示安装界面

frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\PackageInstallerActivity.java

 /**
     * Check if it is allowed to install the package and initiate install if allowed. If not allowed
     * show the appropriate dialog.
     */
    private void checkIfAllowedAndInitiateInstall() {//
        // Check for install apps user restriction first.
        final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
                UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
        if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
            showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
            return;
        } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
            startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
            finish();
            return;
        }
        //add for installer white list start
        boolean caninstall =true;
        if(mPkgInfo.applicationInfo.packageName != null 
            && !isInstallerEnable(mPkgInfo.applicationInfo.packageName)){
            caninstall = false;  
        }
        if(!caninstall){
            Log.w(TAG, "caninstall "+caninstall);
            setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
            Toast.makeText(this, R.string.install_failed_invalid_apk, Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        //add for installer white list end
        if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
            initiateInstall();
        } else {
            // Check for unknown sources restrictions.
            final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
            final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
            final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
                    & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
            if (systemRestriction != 0) {
                showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
            } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
                startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
            } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
                startAdminSupportDetailsActivity(
                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
            } else {
                handleUnknownSources();
            }
        }
    }
    //add for installer white list start
    private boolean isInstallerEnable(String packagename){
        Log.i(TAG, "isInstallerEnable packagename  "+packagename);
        boolean flag=true;
        if(TextUtils.isEmpty(packagename)){
            return flag;
        }
        try{
      Uri mUri = Uri.parse("content://com.android.packageinstaller.app.provider/install_app_whitelist");
            Cursor c = getContentResolver().query(mUri, null, null, null, null);
            if(c == null){
                Log.w(TAG, "isInstallerEnable "+mUri+" no exists ");
                return flag;
            }
            if(c.getCount()!=0){
                flag=false;
                while(c.moveToNext()){
                    String pkgname=c.getString(c.getColumnIndex("pkg_name"));
                    Log.i(TAG, "isInstallerEnable c.moveToNext() "+pkgname);
                    if(packagename.equals(pkgname)){
                        flag=true;
                        break;
                    }
                }
            }else{
                Log.i(TAG, "isInstallerEnable no whiltelist ");
            }
            c.close();
            }catch(Exception e){
                Log.e(TAG, "isInstallerEnable query  Exception",e);
                flag=true;
            }
        return flag;
    }
    //20180503  add for installer white list end

3、在 PackageManagerService 中拦截安装


adb 安装最终会走到 preparePackageLI()


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


    // add for installer white list start
    private boolean isInstallerEnable(String packagename){
        Log.i(TAG, "isInstallerEnable packagename  "+packagename);
        boolean flag=true;
        if(TextUtils.isEmpty(packagename)){
            return flag;
        }
        try{
            Uri mUri = Uri.parse("content://com.android.packageinstaller.app.provider/install_app_whitelist");
            Cursor c = mContext.getContentResolver().query(mUri, null, null, null, null);
            if(c == null){
                Log.w(TAG, "isInstallerEnable "+mUri+" no exists ");
                return flag;
            }
            if(c.getCount()!=0){
                flag=false;
                while(c.moveToNext()){
                    String pkgname=c.getString(c.getColumnIndex("pkg_name"));
                    Log.i(TAG, "isInstallerEnable c.moveToNext() "+pkgname);
                    if(packagename.equals(pkgname)){
                        flag=true;
                        break;
                    }
                }
            }else{
                Log.i(TAG, "isInstallerEnable no whiltelist ");
            }
            c.close();
            }catch(Exception e){
                Log.e(TAG, "isInstallerEnable query  Exception",e);
                flag=true;
            }
        return flag;
    }
    // add for installer white list end
  @GuardedBy("mInstallLock")
    private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
            throws PrepareFailure {
        final int installFlags = args.installFlags;
        final String installerPackageName = args.installerPackageName;
        final String volumeUuid = args.volumeUuid;
        final File tmpPackageFile = new File(args.getCodePath());
        final boolean onExternal = args.volumeUuid != null;
    ......
          //add for installer white list start
        boolean caninstall =true;
        Log.i(TAG, "installPackageLI pkg.packageName "+pkg.packageName);
        if(pkg.packageName != null && !isInstallerEnable(pkg.packageName)){
            caninstall = false;
        }
        if(!caninstall){
            // Toast.makeText(mContext, R.string.install_error, Toast.LENGTH_LONG).show();
            //res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
            throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
                                "Package " + pkg.packageName + " is a forbidden app. "
                                        + "Forbidden apps are not installs.");
        }
        //add for installer white list end
        // If package doesn't declare API override, mark that we have an install
        // time CPU ABI override.
        if (TextUtils.isEmpty(pkg.cpuAbiOverride)) {
            pkg.cpuAbiOverride = args.abiOverride;
        }
        String pkgName = res.name = pkg.packageName;
        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0) {
            if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
                throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
            }
        }
    ......
}



4、在 PackageManagerService 中拦截卸载


不论是 adb 卸载还是通过系统设置拖拽卸载最终都会走 deletePackageX()

  //  add for uninstaller black list start
  private boolean isUnInstallerEnable(String packagename){
      Log.i(TAG, "isUnInstallerEnable packagename  "+packagename);
      boolean flag=true;
      if(TextUtils.isEmpty(packagename)){
          return flag;
      }
      try{
          Uri mUri = Uri.parse("content://com.android.packageinstaller.app.provider/uninstall_app_blacklist");
          Cursor c = mContext.getContentResolver().query(mUri, null,"pkg_name = ? ", new String[]{packagename}, null);
          if(c == null){
              Log.w(TAG, "isUnInstallerEnable "+mUri+" no exists ");
              return flag;
          }
          if(c.moveToFirst()){
              String pkgname=c.getString(c.getColumnIndex("pkg_name"));
              Log.i(TAG, "isUnInstallerEnable c.moveToFirst() "+pkgname);
              flag=false;
          }else{
              Log.i(TAG, "isUnInstallerEnable "+ packagename +" doesn't exists in the blacklist ");
          }
          c.close();
      }catch(Exception e){
          Log.e(TAG, "isUnInstallerEnable query  Exception",e);
          flag=true;
      }
      return flag;
  }
  //  add for uninstaller black list end
    /**
     *  This method is an internal method that could be get invoked either
     *  to delete an installed package or to clean up a failed installation.
     *  After deleting an installed package, a broadcast is sent to notify any
     *  listeners that the package has been removed. For cleaning up a failed
     *  installation, the broadcast is not necessary since the package's
     *  installation wouldn't have sent the initial broadcast either
     *  The key steps in deleting a package are
     *  deleting the package information in internal structures like mPackages,
     *  deleting the packages base directories through installd
     *  updating mSettings to reflect current status
     *  persisting settings for later use
     *  sending a broadcast if necessary
     */
    public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags) {
        final PackageRemovedInfo info = new PackageRemovedInfo(this);
        final boolean res;
        final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0
                ? UserHandle.USER_ALL : userId;
        if (isPackageDeviceAdmin(packageName, removeUser)) {
            Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
            return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
        }
        //  add for uninstaller black list start
        if(!isUnInstallerEnable(packageName)){
            return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
        }
        //  add for uninstaller black list end
        final PackageSetting uninstalledPs;
        final PackageSetting disabledSystemPs;
        final PackageParser.Package pkg;
        // for the uninstall-updates case and restricted profiles, remember the per-
        // user handle installed state
        int[] allUsers;
    ......
}

最后附带一个增、删包名的工具类 AppListHelper.java

package cn.test.app.util;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class AppListHelper {
    private static AppListHelper appListHelper;
    private static final Uri INSTALL_APP_WHITELIST_URI=Uri.parse("content://com.android.packageinstaller.app.provider/install_app_whitelist");
    private static final Uri UNINSTALL_APP_BLACKLIST_URI=Uri.parse("content://com.android.packageinstaller.app.provider/uninstall_app_blacklist");
    public static AppListHelper getInstance(){
        if (appListHelper == null){
            appListHelper = new AppListHelper();
        }
        return appListHelper;
    }
    public void addInstallPackage(Context context, String number){
        ContentResolver contentResolver = context.getContentResolver();
        ContentValues newValues = new ContentValues();
        newValues.put("pkg_name", number);
        contentResolver.insert(INSTALL_APP_WHITELIST_URI, newValues);
    }
    public void removeInstallPackages(Context context, String number) {
        ContentResolver contentResolver = context.getContentResolver();
        contentResolver.delete(INSTALL_APP_WHITELIST_URI,
                "pkg_name = ?",
                new String[] {number});
    }
    public void addUnInstallPackage(Context context, String number){
        ContentResolver contentResolver = context.getContentResolver();
        ContentValues newValues = new ContentValues();
        newValues.put("pkg_name", number);
        contentResolver.insert(UNINSTALL_APP_BLACKLIST_URI, newValues);
    }
    public void removeUnInstallPackages(Context context, String number) {
        ContentResolver contentResolver = context.getContentResolver();
        contentResolver.delete(UNINSTALL_APP_BLACKLIST_URI,
                "pkg_name = ?",
                new String[] {number});
    }
}


目录
相关文章
|
Shell
makefile编写与使用
makefile编写与使用
982 0
makefile编写与使用
|
缓存 开发框架 安全
【Uniapp 专栏】详解 Uniapp 的网络请求功能特性
【5月更文挑战第13天】Uniapp是一款跨平台开发框架,提供便捷的网络请求功能,支持HTTP/HTTPS协议及GET/POST等多种请求方法。它允许设置请求参数、处理响应数据,并有超时时间、缓存策略及错误处理机制。还能与状态管理、页面交互结合,确保数据安全并进行性能优化。通过案例和比较,展现了Uniapp在网络请求上的优势,为开发高质量移动应用奠定基础。理解和掌握这些特性对于创建出色应用体验至关重要。
452 1
【Uniapp 专栏】详解 Uniapp 的网络请求功能特性
|
XML Android开发 数据格式
androidQ(10.0) 读取蓝牙设备当前电量并显示
androidQ(10.0) 读取蓝牙设备当前电量并显示
532 0
|
存储 编解码 API
Android Media Framework(一)OpenMAX 框架简介
OpenMAX IL是Khronos Group为嵌入式和移动设备设计的低层级接口,用于统一调用音频、视频和图像编解码器,确保跨平台兼容性。它包括Core API(管理组件加载和方法调用)和Component API(组件实现,如源、接收器、编解码器等)。组件通过端口进行数据交互,客户端使用Core API加载和控制组件。Android引入OMX IL以支持不同芯片上的编解码器。组件状态包括Loaded、Idle、Executing和Invalid。组件架构涉及参数配置、命令处理和缓冲区管理,数据交换通过回调函数完成,端口持有预分配或组件自分配的缓冲区。
322 0
|
Java API Android开发
android13(T) 三方APP默认授权
android13(T) 三方APP默认授权
866 0
|
Android开发
Android 8.1 允许安装未知来源权限/允许来自此来源的应用
Android 8.1 允许安装未知来源权限/允许来自此来源的应用
823 0
|
XML 存储 搜索推荐
|
机器学习/深度学习 人工智能 JavaScript
IntelliJ IDEA最全设置,最全快捷键以及常用插件
IntelliJ IDEA最全设置,最全快捷键以及常用插件
|
运维 Kubernetes Cloud Native
Higress + Nacos 微服务网关最佳实践
Higress 结合 Nacos 作为微服务网关的实战演示
Higress + Nacos 微服务网关最佳实践
|
Java Android开发 数据安全/隐私保护
android10.0(Q) AOSP 增加应用锁功能
android10.0(Q) AOSP 增加应用锁功能
404 0