实现思路
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});
}
}