Hello, I'm using a Nexus 4, running Android 5.0.0 build LRX21T, with Xposed (xposed-v87-sdk21-arm.zip). I'm trying to run a command early during Android startup. My code so far looks like this:
Code:
package com.example.test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.XposedBridge;
class RunCmdTask extends Thread {
private String cmd;
RunCmdTask(String c) {
cmd = c;
}
public void run() {
XposedBridge.log("task start");
Process proc = null;
try {
Runtime rt = Runtime.getRuntime();
proc = rt.exec(cmd);
proc.waitFor();
BufferedReader stdin = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String s;
while ((s = stdin.readLine()) != null)
XposedBridge.log(s);
stdin.close();
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
XposedBridge.log(sw.toString());
} finally {
if (proc != null)
proc.destroy();
}
XposedBridge.log("task end");
}
}
public class Main implements IXposedHookZygoteInit {
@Override
public void initZygote(StartupParam startupParam) throws Throwable {
XposedBridge.log("main start");
try {
RunCmdTask id = new RunCmdTask("id");
id.start();
id.join();
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
XposedBridge.log(sw.toString());
}
XposedBridge.log("main end");
}
}
The "id" gets invoked, since my logcat looks like:
Code:
I/Xposed (25305): main start
I/Xposed (25305): task start
I/Xposed (25305): uid=0(root) gid=0(root) context=u:r:zygote:s0
I/Xposed (25305): task end
I/Xposed (25305): main end
The problem is that the startup process hangs after this point. I just get the bootup animation looping over and over again. I've tried terminating the VM by calling the Runtime.halt() method, but that kills the whole Zygote, thus being equivalent to a soft reboot. I'm curious to see what I'm mistaking/overlooking. From what I understand, since both the thread and the main hook method finish, normal startup should resume.
Related
I am trying to develop a register system in Android studio, however it registers users but the application closes down as it gives java.lang.NullPointerException error.
Errro Messages
> E/JSON Parser﹕ Error parsing data org.json.JSONException: Value
> 2015-12-09 of type java.lang.String cannot be converted to JSONObject
>
> E/AndroidRuntime﹕ FATAL EXCEPTION: mainPID: 2386
> java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.json.JSONObject.getString(java.lang.String)' on
> a null object reference
Code:
> at com.bradvisor.bradvisor.Register$ProcessRegister.onPostExecute(Register.java:214)
if (json.getString(KEY_SUCCESS) != null) { - Line 214: where I get an error message, not to sure why
> at com.bradvisor.bradvisor.Register$ProcessRegister.onPostExecute(Register.java:171)
private class ProcessRegister extends AsyncTask<String, String, JSONObject> { - Line 171: where I get an error message, not to sure why.
Register.java File.
Code:
private class ProcessRegister extends AsyncTask<String, String, JSONObject> { - Line 171: where I get an error message, not to sure why.
/**
* Defining Process dialog
**/
private ProgressDialog pDialog;
String email,password,fname,lname,uname;
@Override
protected void onPreExecute() {
super.onPreExecute();
inputUsername = (EditText) findViewById(R.id.uname);
inputPassword = (EditText) findViewById(R.id.pword);
fname = inputFirstName.getText().toString();
lname = inputLastName.getText().toString();
email = inputEmail.getText().toString();
uname= inputUsername.getText().toString();
password = inputPassword.getText().toString();
pDialog = new ProgressDialog(Register.this);
pDialog.setTitle("Contacting Servers");
pDialog.setMessage("Registering ...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
@Override
protected JSONObject doInBackground(String... args) {
UserFunctions userFunction = new UserFunctions();
JSONObject json = userFunction.registerUser(fname, lname, email, uname, password);
return json;
}
@Override
protected void onPostExecute(JSONObject json) {
/**
* Checks for success message.
**/
try {
if (json.getString(KEY_SUCCESS) != null) { - Line 214: where I get an error message, not to sure why.
registerErrorMsg.setText("");
String res = json.getString(KEY_SUCCESS);
String red = json.getString(KEY_ERROR);
if(Integer.parseInt(res) == 1){
pDialog.setTitle("Getting Data");
pDialog.setMessage("Loading Info");
registerErrorMsg.setText("Successfully Registered");
DatabaseHandler db = new DatabaseHandler(getApplicationContext());
JSONObject json_user = json.getJSONObject("user");
/**
* Removes all the previous data in the SQlite database
**/
UserFunctions logout = new UserFunctions();
logout.logoutUser(getApplicationContext());
db.addUser(json_user.getString(KEY_FIRSTNAME),json_user.getString(KEY_LASTNAME),json_user.getString(KEY_EMAIL),json_user.getString(KEY_USERNAME),json_user.getString(KEY_UID),json_user.getString(KEY_CREATED_AT));
/**
* Stores registered data in SQlite Database
* Launch Registered screen
**/
Intent registered = new Intent(getApplicationContext(), Registered.class);
/**
* Close all views before launching Registered screen
**/
registered.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pDialog.dismiss();
startActivity(registered);
finish();
}
else if (Integer.parseInt(red) ==2){
pDialog.dismiss();
registerErrorMsg.setText("User already exists");
}
else if (Integer.parseInt(red) ==3){
pDialog.dismiss();
registerErrorMsg.setText("Invalid Email id");
}
}
else{
pDialog.dismiss();
registerErrorMsg.setText("Error occured in registration");
}
} catch (JSONException e) {
e.printStackTrace();
}
}}
public void NetAsync(View view){
new NetCheck().execute();
}}
deleted
Are you sure the object returned from doInBackground is not null ?
@override
protected JSONObject doInBackground(String... args) {
UserFunctions userFunction = new UserFunctions();
JSONObject json = userFunction.registerUser(fname, lname, email, uname, password);(json is null)
return json; ( return null )
}
@override
protected void onPostExecute(JSONObject json) { ( json is null )
/**
* Checks for success message.
**/
try {
if (json.getString(KEY_SUCCESS) != null) { - Line 214: where I get an error message, not to sure why. ( null getString cause exception )
So, I have written an app that emails a file on every boot iteration. I flashed a new ROM and now it isnt working. I reboot and run a logcat on boot and see that the logcat and or gmail is complaining about no e-mail application being installed.
The error seems to be:
Code:
05-06 11:53:52.813 2514 2624 I Gmail : getAccountsCursor
05-06 11:53:52.814 2514 2625 D ActivityThread: Loading provider com.google.android.gmail.provider;com.android.mail.notifier;com.google.android.gm.email.provider;com.google.android.gm.email.notifier: com.android.email.provider.EmailProvider
05-06 11:53:53.282 935 2233 W ActivityManager: Unable to start service Intent { cmp=me.bluemail.mail/com.android.emaileas.service.AttachmentService } U=0: not found
05-06 11:53:53.314 935 2081 W ActivityManager: Unable to start service Intent { cmp=me.bluemail.mail/com.android.emaileas.service.AttachmentService } U=0: not found
05-06 11:53:53.452 2514 2685 I Gmail : No Email application installed
05-06 11:53:53.452 2514 2685 I EmailMigration: No data to migrate
05-06 11:53:53.452 2514 2685 W EmailMigration: No Exchange migration, not the right Email provider version
I do in fact have gmail installed and i went as far as installing a 3rd party email client.
The code I have is an smtp client written by someone else that has been integrated with my code. Like i stated before, everything worked before the flash. I actually figured the issue out last time but cant remember the solution I implemented.
I came from DU 5.1 and moved to DU 6.0. I flashed 4 different ROMs in hopes that it was the ROM but this was not the case.
I am using a rooted Nexus 6 and developing on Linux with no IDE.
The SMTP Client:
GmailSender.java
Code:
package com.keysender;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import android.util.Log;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import java.util.Properties;
import java.security.Security;
import javax.activation.DataSource;
import javax.activation.DataHandler;
import javax.mail.internet.MimeMessage;
import javax.mail.PasswordAuthentication;
import javax.mail.internet.InternetAddress;
public class GmailSender extends javax.mail.Authenticator {
private String mailhost = "smtp.gmail.com";
private String user;
private String password;
private Session session;
static {
Security.addProvider(new com.keysender.JSSEProvider());
}
public GmailSender(String user, String password) {
this.user = user;
this.password = password;
try {
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.host", mailhost);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
props.setProperty("mail.smtp.quitwait", "false");
session = Session.getDefaultInstance(props, this);
}
catch(Exception e) {
e.printStackTrace();
Log.d("ERROR ", e.getMessage(), e);
}
}
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password);
}
public synchronized void sendMail(String subject, String body, String sender, String recipients) throws Exception {
try{
MimeMessage message = new MimeMessage(session);
DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/plain"));
//DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "message/rfc822"));
message.setSender(new InternetAddress(sender));
message.setSubject(subject);
message.setDataHandler(handler);
if (recipients.indexOf(',') > 0)
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients));
else
message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients));
Transport.send(message);
}catch(Exception e){
e.printStackTrace();
}
}
public class ByteArrayDataSource implements DataSource {
private byte[] data;
private String type;
public ByteArrayDataSource(byte[] data, String type) {
super();
this.data = data;
this.type = type;
}
public ByteArrayDataSource(byte[] data) {
super();
this.data = data;
}
public void setType(String type) {
this.type = type;
}
public String getContentType() {
if (type == null)
return "application/octet-stream";
else
return type;
}
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
public String getName() {
return "ByteArrayDataSource";
}
public OutputStream getOutputStream() throws IOException {
throw new IOException("Not Supported");
}
}
}
JSSEProvider.java
Code:
package com.keysender;
import java.security.AccessController;
import java.security.Provider;
public final class JSSEProvider extends Provider {
public JSSEProvider() {
super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {
public Void run() {
put("SSLContext.TLS",
"org.apache.harmony.xnet.provider.jsse.SSLContextImpl");
put("Alg.Alias.SSLContext.TLSv1", "TLS");
put("KeyManagerFactory.X509",
"org.apache.harmony.xnet.provider.jsse.KeyManagerFactoryImpl");
put("TrustManagerFactory.X509",
"org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl");
return null;
}
});
}
}
Relevant code snippet from my email method inside of my BootService class
Code:
try {
GmailSender sender = new GmailSender("[email protected]", "password");
sender.sendMail("keysender", text.toString(), "[email protected]", "[email protected]");
deleteFile();
}
catch(Exception e) {
e.printStackTrace();
Log.e("Mail could not be sent.", e.getMessage(), e);
}
seems like I placed that in a wrong section.Please ,move it to dev section
___
I'm hooking systemui to get data from updateAppMessage method. And I'm hooking method updateMessage method in package launcher. SystemUI's method invokes first (I checked).
All I need is to remember data from systemui hook to use it later in launcher hook. As it turns out it's a big problem to me. I wan't to compare package name from these two methods. But when it goes to updateMessage in launcher I see in log file that all variables from systemui hook reset to their initial values.
May be I miss just one crucial thing but I spend already the whole day so I'm begging for help.
I'm using xposed v87 for miui (android 6) by SolarWarez (also now I have flashed v86 and nothing changed)
here's the log
Code:
12-01 00:19:53.464 I/Xposed ( 5682): OMG [B]pkg= com.vkcoffee.androi[/B]d number=2
12-01 00:19:53.468 I/Xposed ( 5968): Launcher [B]pkg=com.vkcoffee.android[/B]
12-01 00:19:53.468 I/Xposed ( 5968): Launcher other= -1 [B]-string[/B]
12-01 00:20:58.544 I/Xposed ( 5682): OMG pkg= android number=0
12-01 00:20:58.555 I/Xposed ( 5968): Launcher pkg=android
12-01 00:20:58.555 I/Xposed ( 5968): Launcher other= -1 -string
12-01 00:20:58.816 I/Xposed ( 5682): OMG pkg= com.miui.securitycenter number=0
12-01 00:20:58.820 I/Xposed ( 5968): Launcher pkg=com.miui.securitycenter
12-01 00:20:58.820 I/Xposed ( 5968): Launcher other= -1 -string
12-01 00:22:24.126 I/Xposed ( 5968): Launcher pkg=de.robv.android.xposed.installer
12-01 00:22:24.126 I/Xposed ( 5968): Launcher other= -1 -string
and here's the code
PHP:
public class XposedMod implements IXposedHookInitPackageResources, IXposedHookZygoteInit, IXposedHookLoadPackage {
public static String MODULE_PATH = null;
public static int number=-1;
public static String notif_pkg="-string";
public static String pkg="-string";
public static ComponentName comp;
@Override
public void initZygote(IXposedHookZygoteInit.StartupParam startupParam) throws Throwable {
MODULE_PATH = startupParam.modulePath;
XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, null);
}
@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
}
@Override
public void handleLoadPackage(LoadPackageParam lpparam)
throws Throwable {
if (lpparam.packageName.equals("com.miui.home") ) {
findAndHookMethod("com.miui.home.launcher.ApplicationsMessage",lpparam.classLoader,"updateMessage",ComponentName.class, int.class, String.class,String.class, byte[].class, new XC_MethodHook()
{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
comp = (ComponentName) param.args[0];
pkg=comp.getPackageName();
XposedBridge.log("Launcher pkg="+pkg);
XposedBridge.log("Launcher other="+ " "+number+" "+notif_pkg);
}
});
}
if (lpparam.packageName.equals("com.android.systemui")) {
findAndHookMethod("com.android.systemui.statusbar.phone.PhoneStatusBar",lpparam.classLoader,"updateAppMessage","com.android.systemui.statusbar.ExpandedNotification", new XC_MethodHook()
{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
StatusBarNotification sbn =(StatusBarNotification) param.args[0];
number=sbn.getNotification().number;
notif_pkg=sbn.getPackageName();
XposedBridge.log("OMG pkg= "+notif_pkg+" number="+number);
}
});
}
}
}
Also I tried to store data in text File but with no luck as well. My own application Activity returns null context everytime I try to get it from XposedMod class. And I tried to use SystemUI context to store file in /data/data/com.android.systemui/files/ folder (with file.setReadable(true, false); on file creation) but later when I'm trying to read the file xposed log throws exception about "FileNotFoundException.... EACCES (Permition denied)
PHP:
public static String getStringFromFile(String fileName) throws IOException {
File file = new File("/data/data/com.android.systemui/files/", fileName);
int length = (int) file.length();
byte[] bytes = new byte[length];
FileInputStream in = new FileInputStream(file);
try {
in.read(bytes);
} finally {
in.close();
}
String contents = new String(bytes);
return contents;
}
I'm just dumb. If I create file in internal storage there's no access error. but still I want to deal only with variables inside my app..
Note, that you have hooks in 2 different processes. Those are "isolated" so when you set value of variable in one process (systemui) you won't be able to see that value in another process (launcher). Those variables are completely different and independent.
You will have to use some kind of shared storage or some kind of IPC communication method.
The easiest that comes to my mind is e.g. to use Settings.Global storage to save variable in one process and then read that variable in another process.
All you need to access Setting.Global is a valid context which gives you content resolver you need for Setting.Global.putString/getString methods.
C3C076 said:
Note, that you have hooks in 2 different processes. Those are "isolated" so when you set value of variable in one process (systemui) you won't be able to see that value in another process (launcher). Those variables are completely different and independent.
Click to expand...
Click to collapse
thanks for clarifying these things to me
SU Version 2.82 and using BlueStacks 3
I've made a post on StackExchange but w/o any responses - inside are links I'll reference below but unfortunately can't directly post here due to being a new user of these forums. Googling the following should turn up the result: SU Command to start java class for input automation not working
Since then I've learned a bit more about what I'm trying to do (which is surprising considering I've been attempting this since the 30th of January) and implemented a singleton so that I can toast from within the class I'm attempting to start. Regardless of whether this allows me to inject inputs to other apps, my current problem is simply that I'm unable to use a SU command to open up my Main.Java class as described in the following links:
The code describing what I'm trying to do: OmerJerk Execute Java Class as Root User
The code w/ a full implementation: Remotedroid on GitHub
^ ServerService runs MainStarter which runs Main.Java as SU so that Main.Java can run EventInput to inject motion events
The super basic implementation I've got is below, but I've tried a bunch of things. I can't seem to figure out what's going wrong.
A snippet from ActivityMain wherein I'm running the Main.Java (attempting at least):
Code:
new Main().main(COMMAND3); //COMMAND3 is just a String[] because if it's not provided this won't execute. This isn't what I'm trying to do, though. Just a test to see if my Main.Java was broke.
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids){
try {
//BlueStacks is 32 bit so it only has app_process - it doesn't have app_process32 and I believe if you try to target it it simply fails (the symbolic link is inconsistent iirc) vv
String[] COMMAND4 = {"su", "-c", "\"CLASSPATH=" + MainActivity.this.getPackageCodePath(), "/system/bin/app_process", "/system/bin", MainActivity.this.getPackageName() + ".Main"};
java.lang.Process console = Runtime.getRuntime().exec(COMMAND4);
BufferedWriter stdin = new BufferedWriter(new OutputStreamWriter(console.getOutputStream()));
String outputStr = new String();
BufferedReader reader = new BufferedReader(new InputStreamReader(console.getInputStream()));
while (reader.ready()) {
outputStr += reader.readLine();
}
PropertyReader.getInstance().setText(outputStr);
} catch (IOException e) {
e.printStackTrace();
PropertyReader.getInstance().showToast("IOException" + e.getMessage());
}
// final List<String> SUOutput = Shell.SU.run(String.format(COMMAND,
// new String[] {
// getApplicationContext()
// }));
// final String joined = TextUtils.join(", ", SUOutput);
//
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// if (SUOutput != null) {
// Toast.makeText(MainActivity.this, "Output isn't null" + joined, Toast.LENGTH_LONG).show();
// Toast.makeText(MainActivity.this, joined, Toast.LENGTH_LONG).show();
// Toast.makeText(MainActivity.this, SUOutput.toString(), Toast.LENGTH_LONG).show();
//
// } else {
// Toast.makeText(MainActivity.this, "Output is null o-o", Toast.LENGTH_LONG).show();
// }
// }
// });
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "It finished.. ?", Toast.LENGTH_LONG).show();
PropertyReader.getInstance().ToastString();
}
});
return null;
}
}.execute();
}
And my Main.Java is reaaaally simple and very stripped down at this point:
Code:
package intsumniac.overbitegames.com.intsumniac;
import android.os.Process;
public class Main {
public static void main(String[] args) {
PropertyReader.getInstance().showToast("Main Is working!!! SUCCESS" + "current process id = " + Process.myPid() + "current process uid = " + Process.myUid()); //Should be 0, preferably
}
}
It is certainly possible to run Java stuff as root, however you are lacking many contexts/instances/etc. There is some trickery to be able to get around some of that. Some of my apps' root parts are mostly Java, in fact.
Rule of thumb is that most Android API calls are not available, just standard Java things. Toasting for example is most certainly not available.
Have you ever tried to extend your favorite app with new features using Xposed, but were shocked halfway that your hooked app doesn't declare a permission in AndroidManifest ? And then you spent infinite hours on the internet trying to solve this frustrating problem, you decided to use services and an external intent, but you found out that it was not convenient, and finally you gave up...
So you are like me, who wasted hours looking for a solution, until I figured out how to do it myself. Here's a snippet to save time for future Xposed enthusiasts. Put this code snippet in handleLoadPackage
Java:
// Hook will only patch System Framework
if (!lpparam.packageName.equals("android")) return;
String targetPkgName = "com.example.app"; // Replace this with the target app package name
String[] newPermissions = new String[] { // Put the new permissions here
"android.permission.INTERNET",
"android.permission.ACCESS_NETWORK_STATE"
};
String grantPermissionsMethod = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
grantPermissionsMethod = "restorePermissionState";
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
XposedBridge.log("[WARNING] THIS HOOK IS NOT GUARANTEED TO WORK ON ANDROID VERSIONS NEWER THAN ANDROID 12");
}
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
grantPermissionsMethod = "grantPermissions";
}
else {
grantPermissionsMethod = "grantPermissionsLPw";
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
XposedBridge.log("[WARNING] THIS HOOK IS NOT GUARANTEED TO WORK ON ANDROID VERSIONS PRIOR TO JELLYBEAN");
}
}
XposedBridge.hookAllMethods(XposedHelpers.findClass("com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader),
grantPermissionsMethod, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// on Android R and above, param.args[0] is an instance of android.content.pm.parsing.ParsingPackageImpl
// on Android Q and older, param.args[0] is an instance of android.content.pm.PackageParser$Package
// However, they both declare the same fields we need, so no need to check for class type
String pkgName = (String) XposedHelpers.getObjectField(param.args[0], "packageName");
XposedBridge.log("Package " + pkgName + " is requesting permissions");
if (pkgName.equals(targetPkgName)) {
List<String> permissions = (List<String>) XposedHelpers.getObjectField(param.args[0], "requestedPermissions");
for (String newPermission: newPermissions) {
if (!permissions.contains(newPermission)) {
permissions.add(newPermission);
XposedBridge.log("Added " + newPermission + " permission to " + pkgName);
}
}
}
}
});
Notes:
You must check System Framework in LSposed Manager
A reboot is required after adding the target permissions
You still need to prompt the user to accept sensitive permissions (ie android.permission.READ_CONTACTS), even if you have added them using this method
Wow, thx. Great for the install permissions!
I wrote a class to grant install and runtime/sensitive permissions (without prompting users).
Android 12 and 13 implementation:
Java:
public class Grant_Package_Permissions {
private static final int sdk = android.os.Build.VERSION.SDK_INT;
public static void hook(LoadPackageParam lpparam) {
try {
Class<?> PermissionManagerService = XposedHelpers.findClass(
sdk >= 33 /* android 13+ */ ?
"com.android.server.pm.permission.PermissionManagerServiceImpl" :
"com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader);
Class<?> AndroidPackage = XposedHelpers.findClass(
"com.android.server.pm.parsing.pkg.AndroidPackage", lpparam.classLoader);
Class<?> PermissionCallback = XposedHelpers.findClass(
sdk >= 33 /* android 13+ */ ?
"com.android.server.pm.permission.PermissionManagerServiceImpl$PermissionCallback" :
"com.android.server.pm.permission.PermissionManagerService$PermissionCallback", lpparam.classLoader);
// PermissionManagerService(Impl) - restorePermissionState
XposedHelpers.findAndHookMethod(PermissionManagerService, "restorePermissionState",
AndroidPackage, boolean.class, String.class, PermissionCallback, int.class, new XC_MethodHook() {
@SuppressWarnings("unchecked")
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// params
Object pkg = param.args[0];
int filterUserId = (int) param.args[4];
// obtém os campos
Object mState = XposedHelpers.getObjectField(param.thisObject, "mState");
Object mRegistry = XposedHelpers.getObjectField(param.thisObject, "mRegistry");
Object mPackageManagerInt = XposedHelpers.getObjectField(param.thisObject, "mPackageManagerInt");
// Continua ?
String packageName = (String) XposedHelpers.callMethod(pkg, "getPackageName");
Object ps = XposedHelpers.callMethod(mPackageManagerInt,
sdk >= 33 /* android 13+ */ ?
"getPackageStateInternal" :
"getPackageSetting", packageName);
if (ps == null)
return;
int[] getAllUserIds = (int[]) XposedHelpers.callMethod(param.thisObject, "getAllUserIds");
int userHandle_USER_ALL = XposedHelpers.getStaticIntField(Class.forName("android.os.UserHandle"), "USER_ALL");
final int[] userIds = filterUserId == userHandle_USER_ALL ? getAllUserIds : new int[]{filterUserId};
for (int userId : userIds) {
List<String> requestedPermissions;
Object userState = XposedHelpers.callMethod(mState, "getOrCreateUserState", userId);
int appId = (int) XposedHelpers.callMethod(ps, "getAppId");
Object uidState = XposedHelpers.callMethod(userState, "getOrCreateUidState", appId);
// package 1
if (packageName.equals("PACKAGE_1")) {
requestedPermissions = (List<String>) XposedHelpers.callMethod(pkg, "getRequestedPermissions");
grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
Manifest.permission.RECORD_AUDIO);
grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
Manifest.permission.MODIFY_AUDIO_SETTINGS);
}
// package 2
if (packageName.equals("PACKAGE_2")) {
requestedPermissions = (List<String>) XposedHelpers.callMethod(pkg, "getRequestedPermissions");
grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
Manifest.permission.READ_CONTACTS);
}
}
}
});
} catch (Exception e) {
XposedBridge.log(e);
}
}
private static void grantInstallOrRuntimePermission(List<String> requestedPermissions, Object uidState,
Object registry, String permission) {
if (!requestedPermissions.contains(permission))
XposedHelpers.callMethod(uidState, "grantPermission",
XposedHelpers.callMethod(registry, "getPermission", permission));
}
}
Edit: Android 12 and 13 implementation!
this looks promising. how to use this in xposed ? is there a module available?