Android构建系统级应用静默授权,静默安装 您所在的位置:网站首页 怎么取消软件静默设置 Android构建系统级应用静默授权,静默安装

Android构建系统级应用静默授权,静默安装

2024-07-03 13:03| 来源: 网络整理| 查看: 265

文章目录 背景目标实现获取系统应用权限原理实现 静默安装普通升级安装shell命令安装模拟系统安装程序流程

背景

基于定制平板系统开发应用,能获得安卓源码层的支持。 Android8.1系统。

目标 默认获取所需全部权限,不再弹窗询问授权。静默升级安装新版本。 实现 获取系统应用权限

通过sharedUserId这个配置,将当前应用进程配置为系统应用进程,可获取所有权限。

原理

通过Shared User id,拥有同一个UserId的多个APK可以配置成运行在同一个进程中,所以默认就是可以互相访问任意数据,同时可以访问其他APK的数据目录下的数据库和文件,就像访问本程序的数据一样。

为了防止其他应用拿到sharedUserId并滥用,Android系统限制了相同的userId情况下,必须要使用同样的签名才能安装在同一设备上。故,如果要设置为系统共享进程,必须要和系统应用签名一致。

系统给应用签名的逻辑:

系统源码中build/target/product/security目录中有四组默认签名供Android.mk在编译APK使用,通过指定LOCAL_CERTIFICATE字段来实现,分别是:

testkey:普通APK,默认情况下使用。platform:该APK完成一些系统的核心功能。经过对系统中存在的文件夹的访问测试,这种方式编译出来的APK所在进程的UID为system。shared:该APK需要和home/contacts进程共享数据。media:该APK是media/download系统中的一环

普通应用则使用testkey,若要为系统应用,可指定为platform。

原理及介绍参考该文章:Android的权限机制之—— “沙箱”机制sharedUserId和签名

实现

参考该文章实现:Android apk 系统签名

1. 前提: 应用签名不能是自定义签名,需要是系统签名。签名文件需要找系统提供厂商提供,包含platform.pk8、platform.x509.pem、signapk.jar等几个文件,如果这一步不能提供,那通过配置sharedUserId的方式无望。

2、配置sharedUserId 在AndroidManifest.xml中的manifest节点增加:

3、签名 方式一: 在系统源码层,将应用Android.mk中的LOCAL_CERTIFICATE配置为platform,即对应原理中的四个默认类型中的system应用类型

方式二: 打好未签名的apk包,通过命令方式,对应用进行签名。 java -jar signapk.jar platform.x509.pem platform.pk8 unsigned.apk signed.apk 把你对apk名和路径,替换unsigned.apk,signed.apk为输出路径。

方式三: 根据系统方提供的签名文件,生成jks或者keystone文件,后续可直接在AS中打包签名,无需额外再签名。

生成shared.priv.pem文件

openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out shared.priv.pem -nocrypt

生成shared.pk12文件

openssl pkcs12 -export -in platform.x509.pem -inkey shared.priv.pem -out shared.pk12 -name moore

生成jks或者keystone文件

keytool -importkeystore -deststorepass android -destkeypass android -destkeystore moore.jks -srckeystore shared.pk12 -srcstoretype PKCS12 -srcstorepass android -alias moore

生成后则可配置到gradle中,或者打包时选择签名所在路径

4、问题 在实际操作过程中,使用命令方式签名,很大几率会出现no conscrypt_openjdk_jni in java.library.path:的错误,拿到厂商提供的libconscrypt_openjdk_jni.so,重新签名,报另外一个错误:FATAL ERROR in native method: RegisterNatives failed for 'org/conscrypt/NativeCrypto',尝试换电脑、换系统(windows、mac、linux)都无解,厂商那边电脑执行该命令是正常的,遂放弃,采用生成jks签名的方式执行。

5、安装 因更换了签名,若在原安装了该应用的设备上安装,务必提前卸载,同包名不同签名,显然是安装不成功的。

静默安装

应用实现自动更新功能,有几种种安装方式:

调用系统安装程序,传入apk路径(需授权,允许安装应用)执行shell命令静默安装apk(需root权限)使用PackageInstaller代码安装,模拟系统安装程序流程(需为系统应用) 普通升级安装

首先来说,这种方式是会出现安装页面的,并不符合我们的需求,但在安装过程中发现了一些问题,所以还是罗列一下可以拓展参考。

1、方式 下载好升级包apk后,直接使用Intent调起系统安装意图,即可。需注意8.0以上需使用自定义FileProvider,否则无法拿到path路径并且会抛出异常FileUriExposedException。

Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri fileUri = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //自定义的FileProvider,需要在清单文件中声明 String authority = XUpdate.getContext().getPackageName() + ".updateFileProvider"; fileUri = FileProvider.getUriForFile(XUpdate.getContext(), authority, file); } else { fileUri = Uri.fromFile(file); } intent.setDataAndType(fileUri, "application/vnd.android.package-archive"); context.startActivity(intent)

2、问题 问题来了,按照普通应用流程,这个方式是没问题的,一切正常,但我们的应用,是设置过**android:sharedUserId="android.uid.system"**的,那就有点麻烦了。

调起安装的时候,报错:解析软件包时出现问题

确认过下载的安装包无问题,通过文件管理、其他应用等都能调起安装,去除systemUid后也能正常安装,那问题就是这个sharedUserId了。

原因: 通过追踪系统安装流程源码程序,发现如下代码:

final int callingAppId = UserHandle.getAppId(callingUid); if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) { if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) { // Exempted authority for cropping user photos in Settings app } else { Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission" + " grant to " + grantUri + "; use startActivityAsCaller() instead"); return -1; } }

ok,破案,因为我们是设置为SystemUid的应用,但我们的provider并不是com.android.settings.files,所以直接被return -1了,而com.android.settings.files这个provider,是在Settings这个系统应用中定义的,也就是说,只有Settings这个SystemUid的应用能够调起普通的安装流程。

解决方案:

我们并不能够将我们自己的android:authorities改成上面可用的com.android.settings.files,因为个是整个系统唯一的一个值,改成一致是无法安装到设备上的。卒。去除systemUid,能够正常安装,能去除就不会花心思去研究这了。卒。修改源码!在如上判断中,增加一个或条件,加上我们自己的authorities,但要修改源码,有条件可以使用

该问题详细剖析可参考文章:Android android.uid.system的应用调用安装apk失败

shell命令安装

顾名思义,是直接通过输入命令执行的方式进行安装。众所周知Android内核是linux,所以其原理就是使用root账户直接命令安装。 需要先判断是否有root权限:

public static boolean isDeviceRooted() { String su = "su"; String[] locations = {"/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/"}; for (String location : locations) { if (new File(location + su).exists()) { return true; } } return false; }

如果有权限的话,拼接命令,filePath即为apk路径

String command = "pm install -i " + packageName + " --user 0 " + filePath;

然后通过ShellUtils的execCommand方法执行该命令。

如果有Root权限的话,该方式是可行的,不会有界面,但我们对外放出的设备是不会开放root权限的,所以这条路子只能开发测试的时候用,实际上线操作不了。

模拟系统安装程序流程

介绍前可以先参考这两篇文章,了解系统安装应用的流程和代码逻辑 Android包管理机制(一)PackageInstaller的初始化 Android包管理机制(二)PackageInstaller安装APK

我们要做的,就是分析调起系统安装页面,点击安装之后,逻辑层做了些什么操作,然后用直接在我们应用中去实现这个流程。

大致流程

定义一个监听回调,接收安装开始、进度、结束的通知从PackageManager获取PackageInstaller对象,获取apk文件File对象创建一个安装会话Session,得到会话id读取apk File,将文件以流的方式写入session创建一个PendingIntent,用于调起PMS安装逻辑commit这个session,进入PMS安装

PMS安装简介:

接收到会话commit后,会组装成一个PackageInstallObserverAdapter对象,通过Handler发送该消息交由PMS处理PackageManagerService的install逻辑和PMSHandler、IMediaContainerService几个类相关PMSHandler处理绑定消息、新安装消息等逻辑,只有绑定成功才能进行下一步的安装操作通过binder绑定服务,对apk进行解析和复制(耗时操作,安卓系统新开一个service进程)InstallParams和InstallArgs具体执行安装前检查、安装位置确认、copy和move等操作,安装成功后,通过PMS分发消息,通知应用所属用户,更新Settings,为新安装的应用程序准备数据。安装失败则删除apk。断开binder

参考文章::Android包管理机制(三)PMS处理APK的安装

关键代码

int count; int sessionId; byte[] buffer = new byte[65536]; InputStream inputStream; OutputStream outputStream; //拿到apkFile,拿到packageInstaller对象 File apkFile = new File(apkFilePath); long apkFileLength = apkFile.length(); PackageManager pm = context.getPackageManager(); PackageInstaller packageInstaller = pm.getPackageInstaller(); packageInstaller.registerSessionCallback(忽略回调实现); //创建会话 PackageInstaller.Session session = null; PackageInstaller.SessionParams sessionParams; try { sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL); sessionId = packageInstaller.createSession(sessionParams); session = packageInstaller.openSession(sessionId); inputStream = new FileInputStream(apkFile); outputStream = session.openWrite(apkName, 0, apkFileLength); //写入 while ((count = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, count); float progress = ((float) count / (float) apkFileLength); session.setStagingProgress(progress); } session.fsync(outputStream); inputStream.close(); outputStream.flush(); outputStream.close(); //创建PendingIntent Intent intent = new Intent(); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); //绑定并提交 session.commit(pendingIntent.getIntentSender()); } catch (Exception e) { e.printStackTrace(); } finally { if (session != null) { session.abandon(); } }

至此,就能实现静默安装了,但,还有一个问题,正常来说,我们这种系统升级,是需要在升级完成后,重启应用,但是,因为安装过程我们的应用是被Kill掉的,所以,在创建会话时设置的回调,是接收不到安装完成的回调的!!

安装完成重启

思路: 在开始安装的时候,创建一个AlarmManager任务,在10s(视apk大小自己定)后重新打开该应用。

Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()); PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT); AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 6.0及以上 mgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 10000, restartIntent); }

如此,就能实现静默安装,安装完成后重启应用。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有