系统定制-预装软件权限及安装未知应用权限

默认给与预装应用"安装未知应用"权限

本来以为这是个简单的修改,万万没想到这里面坑很大。

APP更新就会使用到此权限,这个功能与普通的动态申请完全不同,正常APP需要实现此功能的话,首先,
1.在清单文件里添加权限:

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

2.用canRequestPackageInstalls()方法判断你的应用是否有这个权限:

boolean haveInstallPermission = getPackageManager().canRequestPackageInstalls();

如果haveInstallPermission 为 true,则说明你的应用有安装未知来源应用的权限,你直接执行安装应用的操作即可。
如果haveInstallPermission 为 false,则说明你的应用没有安装未知来源应用的权限,则无法安装应用。由于这个权限不是运行时权限,所以无法再代码中请求权限,还是需要用户跳转到设置界面中自己去打开权限。
弹出弹窗告知用户,“安装应用需要打开未知来源权限,请去设置中开启权限”,然后用户点击确定之后跳转到未知来源应用权限管理列表:

Uri packageURI = Uri.parse("package:" + getPackageName());
//注意这个是8.0新API
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
startActivityForResult(intent, 666);

然后在onActivityResult中去接收结果:

if (resultCode == RESULT_OK && requestCode == 666) {
	installProcess();//再次执行安装流程,包含权限判等
 }
	//安装应用的流程
    private void installProcess() {
        boolean haveInstallPermission;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //先获取是否有安装未知来源应用的权限
            haveInstallPermission = getPackageManager().canRequestPackageInstalls();
            if (!haveInstallPermission) {//没有权限
                DialogUtils.showDialog(this, "安装应用需要打开未知来源权限,请去设置中开启权限", 
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            startInstallPermissionSettingActivity();
                        }
                    }
                }, null);
                return;
            }
        }
        //有权限,开始安装应用程序
        installApk(apk);
    }

	//安装应用
    private void installApk(File apk) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            intent.setDataAndType(Uri.fromFile(apk), "application/vnd.android.package-				archive");
        } else {//Android7.0之后获取uri要用contentProvider
            Uri uri = AppCommonUtils.getUriFromFile(getBaseContext(), apk);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }

        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        getBaseContext().startActivity(intent);
    }

以上需确认你的系统是8.0或以上,这也是系统新特性。

以上是正常实现功能,那么我的是预置软件,需要默认拥有此权限,首先想到的是在DefaultPermissionGrantPolicy.class里配置权限(不了解的请看下面预装应用给与权限):

private static final Set<String> REQUEST_INSTALL_PACKAGES = new ArraySet<>();
    static {
        REQUEST_INSTALL_PACKAGES.add(Manifest.permission.REQUEST_INSTALL_PACKAGES);
    }
    ......

private void grantDefaultSystemHandlerPermissions(int userId) {
	......
grantRuntimePermissions(app, REQUEST_INSTALL_PACKAGES, userId);
	......	

无效,并且还报错,提示此权限无法动态分配。
普通权限最多动态申请,但是这个正常流程下还需要跳转到系统页面让用户点击开关,就可以看出来与普通权限的区别的,处理也有区别。

第一个方法不行,我就转而去看源码了。根据页面的文字提示,找到RestrictedSwitchPreference,再找到业务类packages/apps/Settings/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.class
找到开关切换的监听:

@Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        final boolean checked = (Boolean) newValue;
        if (preference == mSwitchPref) {
            if (mInstallAppsState != null && checked != mInstallAppsState.canInstallApps()) {
                if (Settings.ManageAppExternalSourcesActivity.class.getName().equals(
                        getIntent().getComponent().getClassName())) {
                    setResult(checked ? RESULT_OK : RESULT_CANCELED);
                }
                setCanInstallApps(checked);
                refreshUi();
            }
            return true;
        }
        return false;
    }

明显setCanInstallApps(checked);就是设置下去的代码:

private void setCanInstallApps(boolean newState) {
        mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
                mPackageInfo.applicationInfo.uid, mPackageName,
                newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
    }

找到AppOpsManager.class内的setMode()函数:

/** @hide */
    @TestApi
    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
    public void setMode(int code, int uid, String packageName, int mode) {
        try {
            mService.setMode(code, uid, packageName, mode);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

	@SystemApi
    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
    public void setMode(String op, int uid, String packageName, int mode) {
        try {
            mService.setMode(strOpToOp(op), uid, packageName, mode);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

里面有俩个函数,刚才调用的是第一个,但是我们需要在外部调用,可以看到代码有/** @hide */,明显不希望外部调用,虽然被注释了。那么我们调用第二个函数。

第一个参数op填:public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
第四个参数mode填:public static final int MODE_ALLOWED = 0;

既然希望默认给与我们的APP这个权限,我想到的是在launch的启动页里加设置此权限的代码,因为setMode()上有权限限制注释:@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
我们先给Launck的清单文件里添加权限:
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
然后在packages/apps/Launcher3/src/com/android/launcher3/Launcher.class里添加代码:

@Override
    protected void onCreate(Bundle savedInstanceState) {
    	......
    	try {
            //给与平板软件安装未知应用的权限
			PackageInfo mPackageInfo = getPackageManager().getPackageInfo("com.android.app", 0);
			AppOpsManager mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
			mAppOpsManager.setMode("android:request_install_packages",
				mPackageInfo.applicationInfo.uid, "com.android.app",0);
        } catch (PackageManager.NameNotFoundException ex) {
            Log.e(TAG, "NameNotFoundException");
        }
        ......

然后发现Launch没有设置此权限的权限,就算给了<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />也没用,原因暂时不明,但是不需要钻牛角尖,我们只需要系统默认给与此权限而已,Launch不行就在Settings的默认拉起项里加也一样,Settings是肯定有此权限对吧。
找到Settings的一个默认拉起项:/packages/apps/TvSettings/Settings/src/com/android/tv/settings/system/FallbackHome.java,这是一个Activity,在onCreate()里添加代码即可:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ......
		try {
            //给与平板软件安装未知应用的权限
			PackageInfo mPackageInfo = getPackageManager().getPackageInfo("com.android.app", 0);
			AppOpsManager mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
			mAppOpsManager.setMode("android:request_install_packages",
				mPackageInfo.applicationInfo.uid, "com.android.app",0);
        } catch (PackageManager.NameNotFoundException ex) {
            Log.e(TAG, "NameNotFoundException");
        }
		......

记得给类里加导入包:

import android.app.AppOpsManager;
import android.content.pm.PackageInfo;

大功告成!

应用分配权限

权限分配对象可分为俩种,第一种是预装的软件,出厂自带的APP,大部分厂商定制系统都需要把公司的APP预装进去,第二种是之后安装的APP

预装应用给与权限

给与预装APP权限需要先找到类
frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.class
类中找到函数grantDefaultSystemHandlerPermissions(),看里面代码应该会很熟悉,里面能找到Camera,Storage provider等等软件的权限给与逻辑,照着写就完事了,例:

		//你的软件
		PackageParser.Package xhorse = null;
        testApp = mServiceInternal.getPackage("com.android.test");
        if (testApp != null
                && doesPackageSupportRuntimePermissions(testApp)) {
            grantRuntimePermissions(testApp, STORAGE_PERMISSIONS, userId);
            grantRuntimePermissions(testApp, CAMERA_PERMISSIONS, userId);
            grantRuntimePermissions(testApp, LOCATION_PERMISSIONS, userId);
            grantRuntimePermissions(testApp, CALENDAR_PERMISSIONS, userId);
            grantRuntimePermissions(testApp, PHONE_PERMISSIONS, userId);
            grantRuntimePermissions(testApp, SMS_PERMISSIONS, userId);
            grantRuntimePermissions(testApp, SENSORS_PERMISSIONS,userId);
            grantRuntimePermissions(testApp, MICROPHONE_PERMISSIONS, userId);
			grantRuntimePermissions(testApp, REQUEST_INSTALL_PACKAGES, userId);
        }

简单明了,不多介绍。

第三方APP给与权限

手动安装的第三方app的权限默认开启是修改PackageManagerService.java。找到函数
void doHandleMessage(Message msg),在case POST_INSTALL的地方修改里面的变量
grantPermissions = true,修改如下:

void doHandleMessage(Message msg) {
			......
case POST_INSTALL: {
	if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);

                    PostInstallData data = mRunningInstalls.get(msg.arg1);
                    final boolean didRestore = (msg.arg2 != 0);
                    mRunningInstalls.delete(msg.arg1);

                    if (data != null) {
                        InstallArgs args = data.args;
                        PackageInstalledInfo parentRes = data.res;
                        
                        //修改grantPermissions为true
						final boolean grantPermissions = true;
                        //final boolean grantPermissions = (args.installFlags
                        //        & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
                        final boolean killApp = (args.installFlags
                                & PackageManager.INSTALL_DONT_KILL_APP) == 0;
                        final boolean virtualPreload = ((args.installFlags
                                & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
                        final String[] grantedPermissions = args.installGrantPermissions;
			......

使之后安装的第三方APP拥有申请的所有权限。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值