public abstract void installPackage(Uri packageURI,IPackageInstallObserver observer, int flags,String installerPackageName);
packageURI:要安装apk的位置,可以是file或者url
observer:当apk安装完成时,观察者被回调得到相应的通知,如果observer为null时,表明安装失败,没有回调。
flags:枚举类表明安装信息。
installerPackageName:安装apk所在应用商城的名称。
public abstract void deletePackage(String packageName,IPackageDeleteObserver observer, int flags);
packageName:需要卸载apk的名称
observer:当卸载apk完成时,observer被回调得到通知
flags:卸载apk的信息
报错说明
抛出异常进程号 | 异常类型 | 异常信息 |
---|---|---|
2155-2155 | com.android.volley.TimeoutError | [BaseServerTask]Server task (GetConfigTask) got error statusCode=-1. [CONTEXT service_id=47 ] |
2150-2352 | java.lang.NullPointerException | Attempt to invoke virtual method ‘void android.content.res.StringBlock.close()’ on a null object reference |
1977-2129 | Denying clipboard access to com.google.android.as, application is not in focus neither is a system service for user 0;Denying clipboard access to com.google.android.googlequicksearchbox, application is not in focus neither is a system service for user 0 | |
14235-15031 | java.net.SocketTimeoutException | failed to connect to play.googleapis.com/2404:6800:4008:801::200a (port 443) from /fec0::c9f:d6fe:4300:6720 (port 38130) after 10000ms |
总而言之,最先在与活动通信的服务开始报错,返回错误状态码;其次抛出空指针异常,说明使用一个为null接口回调对象的调用了虚拟的方法;最后无法访问com.google.android.as和com.google.android.googlequicksearchbox,访问play.googleapis.com超时失败。
PackageManager
Y:\project\plat-aml-android-4-20190613\frameworks\base\core\java\android\content\pm\PackageManager中定义安装卸载接口
ApplicationPackageManager
frameworks\base\core\java\android\app\ApplicationPackageManager.java继承并实现PackageManager,实际调用mPM的接口
PackageManagerService
frameworks\base\services\java\com\android\server\pm\mPM提供接口在PackageManagerService.java中具体实现
AIDL可以实现跨进程的通信,即是通过.aidl接口文件映射另一个进程中某个类,即可访问该类的接口。
调用接口的进程相当于客户端,提供接口实现的进程相当于服务端。注意客户端创建的.aidl文件所在的文件路径必须与其在服务端的路劲一致。因为服务端提供实现接口类对应的.aidl文件给客户端映射访问接口。
在客户端某个路径下新建CalculateInterface.aidl文件
package com.example.aidl.calculate;
interface CalculateInterface {
double doCalculate(double a, doubleb);
}
在Service中具体实现aidl接口
public class CalculateService extends Service {
....
private final CalculateInterface.Stub mBinder = new CalculateInterface.Stub() {
@Override
public double doCalculate(double a, double b) throws RemoteException {
// TODO Auto-generated method stub
Log.e("Calculate", "远程计算中");
Calculate calculate = new Calculate();
double answer = calculate.calculateSum(a, b);
return answer;
}
};
}
在客户端的Activity中要进行绑定服务
public class CalculateClient extends Activity {
....
private CalculateInterface mService;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
logE("disconnect service");
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
logE("connect service");
mService = CalculateInterface.Stub.asInterface(service);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Bundle args = new Bundle();
Intent intent = new Intent("com.example.calculate.CalculateService");
intent.putExtras(args);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
....
btnCalculate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
logE("开始远程运算");
try {
double num1 = Double.parseDouble(etNum1.getText().toString());
double num2 = Double.parseDouble(etNum2.getText().toString());
String answer = "计算结果:" + mService.doCalculate(num1, num2);
tvResult.setTextColor(Color.BLUE);
tvResult.setText(answer);
} catch (RemoteException e) {
}
}
});
}
}
在与Android系统源码相同的路径下创建自己所需要的aidl文件,从Android系统文件直接复制代码内容即可,选取需要使用的接口即可。
在java文件点击右键菜单->New->AIDL即可创建aidl文件。
IPackageDeleteObserver.aidl
package android.content.pm;
oneway interface IPackageDeleteObserver {
void packageDeleted(in String packageName, in int returnCode);
}
IPackageInstallObserver.aidl
package android.content.pm;
oneway interface IPackageInstallObserver {
void packageInstalled(in String packageName, int returnCode);
}
IPackageManager.aidl
package android.content.pm;
interface IPackageManager {
void installPackage(in Uri packageURI, IPackageInstallObserver observer, int flags,
in String installerPackageName);
void deletePackageAsUser(in String packageName, IPackageDeleteObserver observer,
int userId, int flags);
}
因直接创建aidl文件无法映射PackageManager的接口。
IPackageManager的对象是通过ActivityThread(安卓主线程,位于frameworks\base\core\java\android\app下)的getPackageManager方法获得的,查看该方法。
其中,通过ServiceManager.getService(“package”)获得PackageManagerService,通过调用IPackageManager.Stub.asInterface(b)得到IPackageManager的代理类Proxy。
创建IPMServe.java获取IPackageManager,并封装安装、卸载的接口。
package com.android.server;
public class IPMServe {
private static IPackageManager iPackageManager = null;
private static IPMServe instance;
private static final String TAG = "IPMServer";
private IPMServe() {
try {
Class<?> forName = Class.forName("android.os.ServiceManager");
Method method = forName.getMethod("getService", String.class);
IBinder iBinder = (IBinder) method.invoke(null, "package");
iPackageManager = IPackageManager.Stub.asInterface(iBinder);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |
InvocationTargetException e) {
e.printStackTrace();
}
}
public static IPMServe getInstance() {
if (instance == null) {
instance = new IPMServe();
}
return instance;
}
public void installPackage(String apkPath, final Context context) throws RemoteException, PackageManager.NameNotFoundException {
File apkFile = new File(apkPath);
Uri apkUri = Uri.fromFile(apkFile);
PackageManager packageManager = context.getPackageManager();
IPackageInstallObserver installObserver = new IPackageInstallObserver.Stub() {
@Override
public void packageInstalled(String packageName, int returnCode) {
Log.d(TAG, "install packageName:" + packageName);
Log.d(TAG, "install app result:" + PMServe.implementationResult(returnCode));
Toast.makeText(context, "install app " + PMServe.implementationResult(returnCode), Toast.LENGTH_SHORT).show();
}
};
int flags = packageManager.PERMISSION_GRANTED;
PackageInfo packageInfo = packageManager.getPackageInfo(PMServe.getPackageName(apkPath),
packageManager.GET_UNINSTALLED_PACKAGES);
if (packageInfo == null) {
flags = PMServe.INSTALL_REPLACE_EXISTING;
}
String installerPackageName = packageManager.getInstallerPackageName(PMServe.getPackageName(apkPath));
iPackageManager.installPackage(apkUri, installObserver, flags, installerPackageName);
}
public void deletePackage(String apkName, final Context context) throws RemoteException, ClassNotFoundException,
NoSuchMethodException, InvocationTargetException, IllegalAccessException {
PackageManager packageManager = context.getPackageManager();
IPackageDeleteObserver observer = new IPackageDeleteObserver.Stub() {
@Override
public void packageDeleted(String packageName, int returnCode) {
Log.d(TAG, "delete packageName:" + packageName);
Log.d(TAG, "delete app result:" + PMServe.implementationResult(returnCode));
Toast.makeText(context, "delete app " + PMServe.implementationResult(returnCode), Toast.LENGTH_SHORT).show();
}
};
int flags = packageManager.PERMISSION_GRANTED;
iPackageManager.deletePackageAsUser(apkName, observer, getUserId(), flags);
}
public static int getUserId() throws ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
Class<?> userHandle = Class.forName("android.os.UserHandle");
Method method = userHandle.getMethod("myUserId", null);
return method.invoke(null, null) == null ? -1 : (int) method.invoke(null, null);
}
}
创建Service
package com.example.service;
public class IPMService extends Service {
private static final String TAG = "IPMService";
public IPMService() {
}
IPMBinder ipmBinder = new IPMBinder();
public class IPMBinder extends Binder {
public void installPackage(String apkPath, final Context context) throws RemoteException, PackageManager.NameNotFoundException {
IPMServe ipmServe = IPMServe.getInstance();
ipmServe.installPackage(apkPath, context);
}
public void deletePackageAsUser(String packageName, final Context context) throws RemoteException,
ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
IPMServe ipmServe = IPMServe.getInstance();
ipmServe.deletePackage(packageName, context);
}
}
;
@Override
public IBinder onBind(Intent intent) {
return ipmBinder;
}
}
创建客户端,绑定Service,在Activity调用接口实现静默安装
package com.example.ui;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private IPMService.IPMBinder ipmBinder;
private final static String TAG = "MainActivity";
private EditText text;
private List<PackageInfo> packageInfoList = getPackageManager().getInstalledPackages(0);
String apkPath = "https://pdds-cdn.uc.cn/3-0/UCBrowser/1909/9274791232e6bf9c22d580b13f77" +
"3b18/UCBrowser_V12.6.2.1042_android_pf145_(Build190903103023).apk?auth_key=" +
"1569910723-0-0-82d71764ec43d10fb78b261b49ede76b&SESSID=3c62e749bb745a20b737e5" +
"b3b2898a48;String str3 = /storage/sdcard0/baidu.com/UC.apk";
String apkName = "UC.apk";
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG, "service connect....");
ipmBinder = (IPMService.IPMBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG, "service disconnect.....");
ipmBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, IPMService.class);
startService(intent);
bindService(intent, connection, BIND_AUTO_CREATE);
Button installButton = (Button) findViewById(R.id.install_aidl);
Button deleteButton = (Button) findViewById(R.id.delete_aidl);
Button reflectJF = (Button) findViewById(R.id.redirect_jf);
installButton.setOnClickListener(this);
deleteButton.setOnClickListener(this);
reflectJF.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.install_aidl:
installApk();
break;
case R.id.delete_aidl:
deleteApk();
break;
case R.id.redirect_jf:
reflectToJF();
break;
default:
break;
}
}
public void reflectToJF() {
Intent intent = new Intent(getApplicationContext(), JavaReflectActivity.class);
startActivity(intent);
}
public void installApk() {
try {
ipmBinder.installPackage(apkPath, getApplicationContext());
} catch (RemoteException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
public void deleteApk() {
try {
ipmBinder.deletePackageAsUser(apkName, getApplicationContext());
} catch (RemoteException | NoSuchMethodException | InvocationTargetException |
ClassNotFoundException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
在AndroidManifast.xml中要注册权限和添加为系统用户组
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ui"
android:sharedUserId="android.uid.system">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
....
manifest>
因实现静默安装需要获取系统权限,则需要将项目打包.
在AS中的Build->Build Bundle(s)/APK(s)->Build Apk(s)即可将项目打包成apk。打包好的apk在app/build/outputs/apk/debug路径下,如下图所示app-debug.apk即是打包的apk。
将打包的apk即是app-debug.apk放到sign_apk.bat文件的同一目录下,双击bat文件即可对apk签名,从而获取系统权限。但由于每个产品的工具链不同,此bat文件获取的系统权限仅仅适用于咪咕OTT机顶盒。
sign_apk.bat
java -jar signapk.jar platform.x509.pem platform.pk8 app-debug.apk AppManagerSigned.apk
platform.x509.pem platform.pk8即是对应不同平台的工具链;
app-debug.apk需要签名的apk;
AppManagerSigned.apk签名后生成的apk。
将签好名的按照如下操作即可安装到机顶盒
adb root
su
adb shell
#关闭内核打印
echo 0 > /proc/sys/kernel/printk
#mount命令是进入shell才可以使用
#重新挂载vendor目录为可读写
>>mount -o rw,remount /vendor;
#重新挂载system目录为可读写
>>mount -o rw,remount /system;
#如果上面的方式不能成功挂载,退出shell,通过adb remount挂载
>>exit
>>adb remount
#挂载对应的设备
>>adb -s 10.118.3.41:5555 remount
#adb push可以替换原来的apk或者写入新的apk
>>adb push D:\android\demo\test.apk /system/app/test.apk
>>adb shell reboot
>>adb -s 10.118.3.41:5555 shell reboot
adb connect 10.118.4.37
adb shell
echo 1 > /sys/class/remount/need_remount
mount -o remount /system
#Ctrl+C退出shell环境
adb push C:\Users\lxy\Desktop\MIGU签名\security\AppManagerSigned.apk
adb shell
cd system/app
#更改apk读写权限
chmod 644 xxxx.apk
反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
//直接通过一个class的静态变量class获取:
Class cls = String.class;
//通过该实例变量提供的getClass()方法获取:
String s = "Hello";
Class cls = s.getClass();
//如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:
Class cls = Class.forName("java.lang.String");
调用Field.setAccessible(true)
的意思是,别管这个字段是不是public
,一律允许访问。
修改非public
字段,需要首先调用setAccessible(true)
。
public class Main {
public static void main(String[] args) throws Exception {
Object p = new Person("Xiao Ming");
Class c = p.getClass();
Field f = c.getDeclaredField("name");
//获取字段
Object value = f.get(p);
System.out.println(value); // "Xiao Ming"
//设置字段
f.setAccessible(true);
f.set(p, "Xiao Hong");
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
通过Class
实例获取所有Method
信息。Class
类提供了以下几个方法来获取Method
:
Method getMethod(name, Class...)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class...)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)public class Main {
public static void main(String[] args) throws Exception {
Class stdClass = Student.class;
// 获取public方法getScore,参数为String:
System.out.println(stdClass.getMethod("getScore", String.class));
// 获取继承的public方法getName,无参数:
System.out.println(stdClass.getMethod("getName"));
// 获取private方法getGrade,参数为int:
System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));
}
}
class Student extends Person {
public int getScore(String type) {
return 99;
}
private int getGrade(int year) {
return 1;
}
}
class Person {
public String getName() {
return "Person";
}
}
对Method
实例调用invoke
就相当于调用该方法,invoke
的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke
方法传入的第一个参数永远为null
。
public class Main {
public static void main(String[] args) throws Exception {
// String对象:
String s = "Hello world";
// 获取String substring(int)方法,参数为int:
Method m = String.class.getMethod("substring", int.class);
// 在s对象上调用该方法并获取结果:
String r = (String) m.invoke(s, 6);
// 打印调用结果:
System.out.println(r);
/*调用一个静态方法*/
// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "12345");
// 打印调用结果:
System.out.println(n);
}
}
创建服务端反射获取安装/卸载接口,PMServe.java
package com.android.server;
public class PMServe {
final static String TAG = "PMService";
public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
public void installPackageByJavaReflect(final Context context, String packageLocation) {
if (packageLocation == null || packageLocation.equals("")) {
Toast.makeText(context, "Input location of install app", Toast.LENGTH_SHORT).show();
} else {
try {
Uri packageUri;
if (packageLocation.startsWith("http://") || packageLocation.startsWith("https://")) {
File apkFile = new File(packageLocation);
packageUri = Uri.fromFile(apkFile);
} else {
packageUri = Uri.parse(packageLocation);
}
IPackageInstallObserver observer = new IPackageInstallObserver.Stub() {
@Override
public void packageInstalled(String packageName, int returnCode) throws RemoteException {
Log.d(TAG, "install packageName:" + packageName);
Log.d(TAG, "install app result:" + implementationResult(returnCode));
Toast.makeText(context, "install app " + implementationResult(returnCode),
Toast.LENGTH_SHORT).show();
}
};
PackageManager packageManager = context.getPackageManager();
int flags = packageManager.PERMISSION_GRANTED;
PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(packageLocation),
packageManager.GET_UNINSTALLED_PACKAGES);
if (packageInfo == null) {
flags = INSTALL_REPLACE_EXISTING;
}
String installerPackageName = packageManager.getInstallerPackageName(getPackageName(packageLocation));
Class appPackageManager = Class.forName("android.app.ApplicationPackageManager");
Method method = appPackageManager.getMethod("installPackage", Uri.class,
IPackageInstallObserver.class, int.class, String.class);
method.invoke(appPackageManager, packageUri, observer, flags, installerPackageName);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException |
ClassNotFoundException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
public static String implementationResult(int returnCode) {
if (returnCode == 1) {
return "success";
} else {
return "failed";
}
}
public void deletePackage(final Context context, String packageName) {
if (packageName == null || packageName.equals("")) {
Toast.makeText(context, "Input name of delete app", Toast.LENGTH_SHORT).show();
} else {
try {
PackageManager packageManager = context.getPackageManager();
IPackageDeleteObserver observer = new IPackageDeleteObserver.Stub() {
@Override
public void packageDeleted(String packageName, int returnCode) throws RemoteException {
Log.d(TAG, "delete packageName:" + packageName);
Log.d(TAG, "delete app result:" + implementationResult(returnCode));
Toast.makeText(context, "delete app " + implementationResult(returnCode),
Toast.LENGTH_SHORT).show();
}
};
int flags = packageManager.PERMISSION_GRANTED;
PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
packageManager.GET_UNINSTALLED_PACKAGES);
if (packageInfo == null) {
Toast.makeText(context, "deleting app isn't exist", Toast.LENGTH_SHORT).show();
} else {
Class appPackageManager = Class.forName("android.app.ApplicationPackageManager");
Method method = appPackageManager.getMethod("deletePackage", String.class,
IPackageDeleteObserver.class, int.class);
method.invoke(packageManager, packageName, observer, flags);
}
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException |
ClassNotFoundException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
public static String getPackageName(String url) {
String rs = null;
if (url.contains("/")) {
String[] urlList = url.split("/");
for (String item : urlList) {
if (item.contains(".apk?")) {
rs = item.substring(0, item.indexOf("?"));
} else if (item.contains(".apk") && (!url.contains(".apk?"))) {
rs = item;
}
}
} else if (url.contains("\\")) {
rs = url.substring(url.lastIndexOf("\\") + 1);
}
return rs;
}
}
创建服务,PMService.java
package com.example.service;
public class PMService extends Service {
private PMServe pmServe = new PMServe();
public PMService() {
}
private PMBinder pmBinder = new PMBinder();
public class PMBinder extends Binder {
public void installApp(Context context, String packageLocation) {
pmServe.installPackageByJavaReflect(context, packageLocation);
}
public void deleteApp(Context context, String packageLocation) {
pmServe.deletePackage(context, packageLocation);
}
}
@Override
public IBinder onBind(Intent intent) {
return pmBinder;
}
}
在客户端绑定Service调用接口实现静默安装/卸载
package com.example.ui;
public class JavaReflectActivity extends AppCompatActivity implements View.OnClickListener {
private PMService.PMBinder mPM;
String packageLocation = "http://fastsoft.onlinedown.net/down/Fcloudmusicsetup2.5.5.197764.exe";
String packageName = "Fcloudmusicsetup2.5.5.197764.exe";
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mPM = (PMService.PMBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mPM = null;
}
};
private EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java_reflect);
Button install = (Button) findViewById(R.id.install_jf);
Button delete = (Button) findViewById(R.id.uninstall_jf);
install.setOnClickListener(this);
delete.setOnClickListener(this);
Intent intent = new Intent(this, PMService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.install_jf:
mPM.installApp(getApplicationContext(), packageLocation);
break;
case R.id.uninstall_jf:
mPM.deleteApp(getApplicationContext(), packageName);
break;
default:
break;
}
}
}