最近弄项目都是些要改源码才能实现的,像静默安装和白名单功能.
静默安装:
1:在源码的AndroidMainfest.xml中添加权限
--- a/frameworks/base/core/res/AndroidManifest.xml
+++ b/frameworks/base/core/res/AndroidManifest.xml
@@ -2451,6 +2451,10 @@
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_PACKAGES"
android:protectionLevel="signature|privileged" />
+ <permission android:name="android.permission.HIDE_INSTALL_PACKAGES"
+ android:protectionLevel="normal" />
+ <permission android:name="android.permission.HIDE_UNINSTALL_PACKAGES"
+ android:protectionLevel="normal" />
2 :在IPackManger.Stub中添加权限判断
--- a/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11704,7 +11704,13 @@ public class PackageManagerService extends IPackageManager.Stub {
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, int userId) {
android.util.SeempLog.record(90);
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+ //mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+ if(mContext.checkCallingPermission(android.Manifest.permission.HIDE_INSTALL_PACKAGES) == PackageManager.PERMISSION_GRANTED) {
+ Slog.i(TAG, "installerPackageName: checkCallingPermission "+installerPackageName);
+ } else {
+ Slog.i(TAG, "installerPackageName: checkCallingPermission PERMISSION_DENIED"+PackageManager.PERMISSION_DENIED);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+ }
3:在APP的libs添加pminstall.jar
4:在APP中添加<uses-permission android:name="android.permission.HIDE_INSTALL_PACKAGES" />权限
静默安装就成功了!!!撒花庆祝。
demo的连接地址:https://github.com/ChloeDimen/pmApplication
白名单功能:因为也是在在源码中修改,查了很多资料和自己的理解,有可能有问题。希望大家理解,有问题大家在评论中提出。
本人希望白名单是可以在SD卡中修改的,查资料是把白名单打包到系统的,所有自己修改验证。
先说下android的apk安装方式,在修改源码。
1:直接调用安装接口
-
Uri mPackageURI = Uri.fromFile( new File(Environment.getExternalStorageDirectory() + apkName));
-
-
int installFlags = 0;
-
PackageManager pm = getPackageManager();
-
try{
-
PackageInfo pi = pm.getPackageInfo(packageName,
-
PackageManager.GET_UNINSTALLED_PACKAGES);
-
if(pi != null) {
-
installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;
-
}
-
}
-
catch (NameNotFoundException e){}
-
PackageInstallObserver observer = new PackageInstallObserver();
-
pm.installPackage(mPackageURI, observer, installFlags);
这种方式一般在APP中,我们是用AIDl的集成,在调用pm.installpackage(mPackageURI, observer, installFlags)。不过要配置很多东西,AIDl导入也要很多错误,要自己修改。app还需要在AndroidManifest增加一句 android:sharedUserId=”android.uid.system”,而且还要用系统的签名工具。因为每个版本和每种不同手机的签名工具不一样,所有在手机上不适合。在市面上像完成静默安装,我知道的只有开发应用市场类的APP。
我现在可以修改源码,就是在自己的板子上开发了!!!
2:通过intent机制,调用packageInstall进行安装(有弹框)
-
String fileName = Environment .getExternalStorageDirectory() + apkName;
-
Uri uri = Uri .fromFile( new File(fileName));
-
Intent intent = new Intent(Intent .ACTION_VIEW);
-
intent .setDataAndType(Uri, application/vnd .android .package -archive ");
-
startActivity(intent)
3:通过命令进行安装 pm install
修改源码:
1:packageMangerServer修改
源码位置:frameworks\base\services\core\java\com\android\server\pm\PackageMangerServer.java,对源码不熟悉找位置找了好久(后面的现在方便了)。
1)添加函数判断
/*add for installer white list*/
private boolean isInstallerEnable(String packagename){
ArrayList<String> whiteListApp = new ArrayList<String>();
Log.e(TAG, "mCallingApp:==5 " );
try{
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream("/sdcard/WhiteListAppFilter.properties")));
Log.e(TAG, "mCallingApp:==6 " );
String line ="";
while ((line = br.readLine()) != null){
Log.e(TAG, "mCallingApp:==7 " );
whiteListApp.add(line);
Log.e(TAG, "mCallingApp:==8 " );
}
br.close();
}catch(java.io.FileNotFoundException ex){
return false;
}catch(java.io.IOException ex){
return false;
}
Log.e(TAG, "mCallingApp:==9 " );
Iterator<String> it = whiteListApp.iterator();
Log.e(TAG, "mCallingApp:==10 " );
while (it.hasNext()) {
Log.e(TAG, "mCallingApp:==11 " );
String whitelisItem = it.next();
Log.e(TAG, "mCallingApp:==12 "+" ,whitelisItem"+whitelisItem+" ,packagename"+packagename );
if (whitelisItem.equals(packagename)) {
Log.e(TAG, "mCallingApp:==13 " );
return true;
}
}
Log.e(TAG, "mCallingApp:==14 " );
return false;
}
isWhiteListApp函数会去读取白名单文件/system/etc/whitelistapps,然后和我们传进来的包名进行匹配,在白名单中返回true,其他情况均返回false。这是在白名单不可以给别人修改的,我要在SD卡中"/sdcard/WhiteListAppFilter.properties"),自己修改,下面就统一这个地址。日志就不删除了,偷懒中。
重要的:要导包,查资料又是没有,苦逼。好的自己加
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
2)获取调用的包名判断是否在白名单中
接下来要在installPackageLI函数对调用安装的apk进行匹配,判断是否在白名单中,如果不在的话则提示错误。注释start和end的代码。
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
final int installFlags = args.installFlags;
String installerPackageName = args.installerPackageName;
File tmpPackageFile = new File(args.getCodePath());
boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);
boolean replace = false;
final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
// Result object to be returned
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
。。。。。。。。
try {
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
// start
if(!isInstallerEnable(pkg.packageName)) {
Log.e(TAG, "mCallingApp:==1 "+pkg.packageName);
res.setError(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"app is not in the whitelist. packageName:" + pkg.packageName);
Log.e(TAG, "mCallingApp:==2 " );
return;
}
// end
/* If the installer passed in a manifest digest, compare it now. */
if (args.manifestDigest != null) {
if (DEBUG_INSTALL) {
final String parsedManifest = pkg.manifestDigest == null ? "null"
: pkg.manifestDigest.toString();
Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
+ parsedManifest);
}
3)添加白名单
/system/etc/whitelistapps内容如下,在编译时可以在mk中修改拷贝到etc目录下,例如下面就是允许这三个包名有安装权限。
com.xxx.xxx1
com.xxx.xxx2
com.xxx.xxx3
这是在系统的,在SD卡中只要改成"/sdcard/WhiteListAppFilter.properties")
2、packageInstall的修改
还是参考packageManagerService的修改,增加isInstallerEnable函数,去读取白名单文件/"/sdcard/WhiteListAppFilter.properties"),然后进行包名匹配,在白名单中返回true,其他情况均返回false。
我们在packageInstaller的PackageInstallerActivity.java中增加以下修改// add for installer enable/disable ,不在白名单中的app,会直接提示不允许安装后退出。
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
。。。。。。。。
//set view
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mOriginatingUid = getOriginatingUid(intent);
// add for installer enable/disable
if (!isInstallerEnable(mPkgInfo.packageName)) {
Log.e(TAG, "mCallingApp:==1 " );
//Toast.makeText(this, R.string.install_not_allow, Toast.LENGTH_LONG).show();
this.finish();
Log.e(TAG, "mCallingApp:==2 " );
}
// Block the install attempt on the Unknown Sources setting if necessary.
if (!requestFromUnknownSource) {
initiateInstall();
return;
}
// If the admin prohibits it, or we're running in a managed profile, just show error
// and exit. Otherwise show an option to take the user to Settings to change the setting.
final boolean isManagedProfile = mUserManager.isManagedProfile();
if (!unknownSourcesAllowedByAdmin
|| (!unknownSourcesAllowedByUser && isManagedProfile)) {
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else if (!unknownSourcesAllowedByUser) {
// Ask user to enable setting first
showDialogInner(DLG_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else {
initiateInstall();
}
}
/** Get the ApplicationInfo for the calling package, if available */
private ApplicationInfo getSourceInfo() {
String callingPackage = getCallingPackage();
if (callingPackage != null) {
try {
return mPm.getApplicationInfo(callingPackage, 0);
} catch (NameNotFoundException ex) {
// ignore
}
}
return null;
}
3、pm install 的修改
禁止pm install,因为有些APK安装竟然是调用pm install命令去安装的。
修改要在pm.java修改,修改方法和上面基本一致。
可以看到,pm install其实调用的是run再去判断参数。
最后还是调用
mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
installerPackageName, verificationParams, abi, userId);应该修改了packagemangerServer.java 就可以了。
4、在网上还找到一种方式(有带验证),不过感觉还跟简单
1.定义一些全局变量,文件位置:
Build.java (frameworks\base\core\java\android\os)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 包管理方式名称<br>
* whitelist: 白名单方式
* certificate: 证书认证方式
* none: 不进行管理
*/
public
static
String packageManage =
"none"
;
/**
* 允许 Launch 显示的 APP 及 APP 白名单
*/
public
static
String[] packageAllow =
new
String[]{
"com.baidu.searchbox"
,
"com.thinta.product.thintazlib"
,
"com.thinta.product.x4usertool"
};
/**
* 允许 Launch 显示的 APP的 证书存放路径
*/
public
static
String certificatePath =
"/system/etc/security/media.zip"
;
|
2.修改安装APK过程,在安装过程添加验证
修改文件的位置:
PackageManagerService.java (frameworks\base\services\core\java\com\android\server\pm)
首先添加一个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
private
static
HashSet<X509Certificate> getTrustedCerts(File keystore)
throws
IOException, GeneralSecurityException {
HashSet<X509Certificate> trusted =
new
HashSet<X509Certificate>();
if
(keystore ==
null
) {
return
trusted;
}
ZipFile zip =
new
ZipFile(keystore);
try
{
CertificateFactory cf = CertificateFactory.getInstance(
"X.509"
);
Enumeration<?
extends
ZipEntry> entries = zip.entries();
while
(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
InputStream is = zip.getInputStream(entry);
try
{
trusted.add((X509Certificate) cf.generateCertificate(is));
}
finally
{
is.close();
}
}
}
finally
{
zip.close();
}
return
trusted;
}
|
修改的函数:private void installPackageLI(InstallArgs args, PackageInstalledInfo res)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
第一处修改:
if
(Build.ThintaCust.packageManage.equals(
"certificate"
))
tmp_flags = PackageManager.GET_SIGNATURES;
final
int
parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK :
0
)
| (onSd ? PackageParser.PARSE_ON_SDCARD :
0
) | tmp_flags;
第二处修改:
if
(Build.ThintaCust.packageManage.equals(
"none"
)){
Log.d(
"XYP_DEBUG"
,
"packageManage = none \n"
);
}
else
if
(Build.ThintaCust.packageManage.equals(
"whitelist"
)){
Log.d(
"XYP_DEBUG"
,
"packageManage = whitelist \n"
);
List<String> list = Arrays.asList(Build.ThintaCust.packageAllow);
if
(list.contains(pkg.packageName)){
Log.d(
"XYP_DEBUG"
,
"can install \n"
);
}
else
{
Log.d(
"XYP_DEBUG"
,
"forbid install \n"
);
res.setError(PackageManager.INSTALL_FAILED_USER_RESTRICTED,
"installPackageLI, forbid install"
);
return
;
}
}
else
if
(Build.ThintaCust.packageManage.equals(
"certificate"
)){
int
verify_pass =
0
;
try
{
File file =
new
File(Build.ThintaCust.certificatePath);
HashSet<X509Certificate> trusted = getTrustedCerts(file);
CertificateFactory cf = CertificateFactory.getInstance(
"X.509"
);
for
(X509Certificate c : trusted) {
String tmp_public_key = c.getPublicKey().toString();
for
(Signature sig : pkg.mSignatures)
{
X509Certificate cert = (X509Certificate)cf.generateCertificate(
new
ByteArrayInputStream(sig.toByteArray()));
String tmp_key = cert.getPublicKey().toString();
if
(tmp_public_key.equals(tmp_key)){
verify_pass =
1
;
break
;
}
}
if
(verify_pass ==
1
)
break
;
}
if
(verify_pass !=
1
){
Log.d(
"XYP_DEBUG"
,
"forbid install \n"
);
res.setError(PackageManager.INSTALL_FAILED_USER_RESTRICTED,
"installPackageLI, forbid install"
);
return
;
}
}
catch
(FileNotFoundException e){
Log.d(
"XYP_DEBUG"
, e.toString());
}
catch
(CertificateException e){
Log.d(
"XYP_DEBUG"
, e.toString());
}
catch
(IOException e){
Log.d(
"XYP_DEBUG"
, e.toString());
}
catch
(GeneralSecurityException e){
Log.d(
"XYP_DEBUG"
, e.toString());
}
}
|
3.证书的压缩方式:
zip -r media.zip media.x509.pem
直接用命令把*.x509.pem 打包成zip文件,然后放到目标板的合适位置;
用第一步中的certificatePath指向存放该zip文件的位置。