Android 8.1 允许安装未知来源权限/允许来自此来源的应用

简介: Android 8.1 允许安装未知来源权限/允许来自此来源的应用

之前 6.0 的未知来源权限是一个总的权限,现在单独分开了具体到 app 对应的权限了。具体可见截图

2020010309293772.png

安装未知来源权限其实就是这货 Manifest.permission.REQUEST_INSTALL_PACKAGES,具体的修改代码方案已经在上篇 Android9.0/8.1/6.0 默认给系统 app 授予所有权限中提供了。这篇只是分析解题思路。


核心方法如下


 if (checkInstallPackagesPermission(pkgName, mPackageInfo)) {
                     Log.e(TAG, pkgName + " need grant INSTALL_PACKAGES permission");
                     mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
                        mPackageInfo.applicationInfo.uid, pkgName, AppOpsManager.MODE_ALLOWED);
                     Log.e(TAG, "grant INSTALL_PACKAGES permission done");
}
private static boolean checkInstallPackagesPermission(String packageName, PackageInfo mPackageInfo){
        int uid = mPackageInfo.applicationInfo.uid;
        //boolean permissionGranted = hasPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES, uid);
        boolean permissionRequested = hasRequestedAppOpPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES, packageName);
        int appOpMode = getAppOpMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid, packageName);
        return appOpMode != AppOpsManager.MODE_DEFAULT || permissionRequested;
    }
    private static int getAppOpMode(int appOpCode, int uid, String packageName) {
        return mAppOpsManager.checkOpNoThrow(appOpCode, uid, packageName);
    }
    private static boolean hasRequestedAppOpPermission(String permission, String packageName) {
        try {
            String[] packages = mIpm.getAppOpPermissionPackages(permission);
            return ArrayUtils.contains(packages, packageName);
        } catch (Exception exc) {
            Log.e(TAG, "PackageManager dead. Cannot get permission info");
            return false;
        }
    }


从 Settings 说起,我们看见的设置界面中有允许未知来源的 Preference,经过搜索找到 InstalledAppDetails,允许未知来源是动态增加的 Preference ,看如下代码


vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\applications\InstalledAppDetails.java


 private void addDynamicPrefs() {
        if (UserManager.get(getContext()).isManagedProfile()) {
            return;
        }
    ...
     boolean isPotentialAppSource = isPotentialAppSource();
    if (isPotentialAppSource) {
                Preference pref = new Preference(getPrefContext());
                pref.setTitle(R.string.install_other_apps);
                pref.setKey("install_other_apps");
                pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                    @Override
                    public boolean onPreferenceClick(Preference preference) {
                        startAppInfoFragment(ExternalSourcesDetails.class,
                                getString(R.string.install_other_apps));
                        return true;
                    }
                });
                category.addPreference(pref);
            }
        }
        addAppInstallerInfoPref(screen);
        maybeAddInstantAppButtons();
 }
    private boolean isPotentialAppSource() {
        AppStateInstallAppsBridge.InstallAppsState appState =
                new AppStateInstallAppsBridge(getContext(), null, null)
                        .createInstallAppsStateFor(mPackageName, mPackageInfo.applicationInfo.uid);
        return appState.isPotentialAppSource();
    }


isPotentialAppSource 值决定当前 app 详情页面是否需要显示允许来自此来源的应用,isPotentialAppSource() 中初始化了 AppStateInstallAppsBridge对象,并由该对象的isPotentialAppSource()返回。

vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\applications\AppStateInstallAppsBridge.java

InstallAppsState createInstallAppsStateFor(String packageName, int uid) {
        final InstallAppsState appState = new InstallAppsState();
        appState.permissionRequested = hasRequestedAppOpPermission(
                Manifest.permission.REQUEST_INSTALL_PACKAGES, packageName);
        appState.permissionGranted = hasPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES,
                uid);
        appState.appOpMode = getAppOpMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid,
                packageName);
        return appState;
    }
public static class InstallAppsState {
        boolean permissionRequested;
        boolean permissionGranted;
        int appOpMode;
    public InstallAppsState() {
              this.appOpMode = AppOpsManager.MODE_DEFAULT;
      }
    ....
    public boolean isPotentialAppSource() {
            Log.e("ExternalSources","appOpMode="+(appOpMode != AppOpsManager.MODE_DEFAULT));
            Log.e("ExternalSources","permissionRequested="+permissionRequested);
            return appOpMode != AppOpsManager.MODE_DEFAULT || permissionRequested;
        }
    ....
}


InstallAppsState 构造函数初始化将赋值 appOpMode = AppOpsManager.MODE_DEFAULT,


appOpMode 的取值有


public static final int MODE_ALLOWED = 0;

public static final int MODE_IGNORED = 1;

public static final int MODE_ERRORED = 2;

public static final int MODE_DEFAULT = 3;


isPotentialAppSource() 的返回值取决于 appOpMode 和 permissionRequested,这两值在 createInstallAppsStateFor() 被重新赋值,继续看对应的方法

public AppStateInstallAppsBridge(Context context, ApplicationsState appState,
            Callback callback) {
        super(appState, callback);
        mIpm = AppGlobals.getPackageManager();
        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    }
private boolean hasRequestedAppOpPermission(String permission, String packageName) {
        try {
            Log.e(TAG, "packageName "+packageName);
            String[] packages = mIpm.getAppOpPermissionPackages(permission);
            for (String pck : packages) {
                Log.e(TAG, "PackageManager "+pck);
            }
            return ArrayUtils.contains(packages, packageName);
        } catch (RemoteException exc) {
            Log.e(TAG, "PackageManager dead. Cannot get permission info");
            return false;
        }
 }
private int getAppOpMode(int appOpCode, int uid, String packageName) {
        return mAppOpsManager.checkOpNoThrow(appOpCode, uid, packageName);
 }

通过 AppOpsManager 获取当前 app 的模式


通过 IPackageManager 获取包含 REQUEST_INSTALL_PACKAGES 权限的包名数组,判断当前包名是否在其中


好了,是否需要显示此权限的逻辑搞清楚了,接下来再看如何授权?


InstalledAppDetails 中 Preference 点击事件对应


startAppInfoFragment(ExternalSourcesDetails.class, getString(R.string.install_other_apps));


对应的页面为 ExternalSourcesDetails


vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\applications\ExternalSourcesDetails.java

@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;
    }
private void setCanInstallApps(boolean newState) {
        mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
                mPackageInfo.applicationInfo.uid, mPackageName,
                newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
    }


点击 RestrictedSwitchPreference 时通过 AppOpsManager 修改 mode 为 AppOpsManager.MODE_ALLOWED

目录
相关文章
|
3天前
|
IDE Java 开发工具
深入探索安卓应用开发:从环境搭建到第一个"Hello, World!"应用
本文将引导读者完成安卓应用开发的初步入门,包括安装必要的开发工具、配置开发环境、创建第一个简单的安卓项目,以及解释其背后的一些基本概念。通过一步步的指导和解释,本文旨在为安卓开发新手提供一个清晰、易懂的起点,帮助读者顺利地迈出安卓开发的第一步。
175 64
|
3天前
|
存储 Java Android开发
探索安卓应用开发:构建你的第一个"Hello World"应用
【9月更文挑战第24天】在本文中,我们将踏上一段激动人心的旅程,深入安卓应用开发的奥秘。通过一个简单而经典的“Hello World”项目,我们将解锁安卓应用开发的基础概念和步骤。无论你是编程新手还是希望扩展技能的老手,这篇文章都将为你提供一次实操体验。从搭建开发环境到运行你的应用,每一步都清晰易懂,确保你能顺利地迈出安卓开发的第一步。让我们开始吧,探索如何将一行简单的代码转变为一个功能齐全的安卓应用!
|
8天前
|
开发框架 搜索推荐 开发工具
打造个性化安卓应用:从零开始的Flutter之旅
【8月更文挑战第51天】本文是一篇面向初学者的Flutter入门教程,旨在通过简单易懂的语言和实际代码示例,引导读者步入跨平台移动应用开发的世界。文章首先介绍了Flutter的基本概念和优势,然后逐步展示了如何搭建开发环境、创建第一个Flutter应用,并实现了一个简单的待办事项列表。最后,文章探讨了Flutter在实现高性能和美观界面方面的潜力,鼓励读者发挥创意,探索更多可能。
53 15
|
8天前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
28 5
|
13天前
|
Android开发 开发者 Kotlin
探索安卓开发中的新特性
【9月更文挑战第14天】本文将引导你深入理解安卓开发领域的一些最新特性,并为你提供实用的代码示例。无论你是初学者还是经验丰富的开发者,这篇文章都会给你带来新的启示和灵感。让我们一起探索吧!
|
8天前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
31 10
|
2天前
|
存储 开发工具 Android开发
使用.NET MAUI开发第一个安卓APP
【9月更文挑战第24天】使用.NET MAUI开发首个安卓APP需完成以下步骤:首先,安装Visual Studio 2022并勾选“.NET Multi-platform App UI development”工作负载;接着,安装Android SDK。然后,创建新项目时选择“.NET Multi-platform App (MAUI)”模板,并仅针对Android平台进行配置。了解项目结构,包括`.csproj`配置文件、`Properties`配置文件夹、平台特定代码及共享代码等。
|
10天前
|
Java Linux Android开发
深入理解Android开发:从基础到高级
【9月更文挑战第17天】本文将深入探讨Android开发的各个方面,包括应用开发、操作系统等。我们将通过代码示例来展示如何创建一个简单的Android应用,并解释其背后的原理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和启示。
|
7天前
|
存储 Java Android开发
🔥Android开发大神揭秘:从菜鸟到高手,你的代码为何总是慢人一步?💻
在Android开发中,每位开发者都渴望应用响应迅速、体验流畅。然而,代码执行缓慢却是常见问题。本文将跟随一位大神的脚步,剖析三大典型案例:主线程阻塞导致卡顿、内存泄漏引发性能下降及不合理布局引起的渲染问题,并提供优化方案。通过学习这些技巧,你将能够显著提升应用性能,从新手蜕变为高手。
15 2
|
8天前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
35 1