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.
Related
(Unfortunately I couldn't post in the relevant thread because of forum permissions)
I'm using Semaphore ICS kernel 1.2 on my Galaxy S (Dark Knight 5.5). I tried to use the Android USB Host API. But UsbManager fails to enumerate all my devices.
I tried to circumvent the "normal" way by constructing and hacking my way by constructing UsbDevice, UsbInterface and UsbInterface manually by scanning the filesystem (/sys/bus/usb/devices)
In the end I still hit a wall -
Code:
public UsbDeviceConnection openDevice(UsbManager manager, UsbDevice device)
{
String devname = device.getDeviceName();
try
{
Field f = UsbManager.class.getDeclaredField("mService");
f.setAccessible(true);
Object service = f.get(manager);
if(service == null)
return null;
Class<?> IUsbManager = Class.forName("android.hardware.usb.IUsbManager");
Method opendevice = IUsbManager.getMethod("openDevice", new Class<?>[] { String.class });
opendevice.setAccessible(true);
ParcelFileDescriptor pfd = (ParcelFileDescriptor)opendevice.invoke(service, new Object[] { devname });
if(pfd == null)
return null;
Class<?> UsbDevConnection = UsbDeviceConnection.class;
Constructor<?> ctor = UsbDevConnection.getConstructor(new Class<?>[] { String.class, ParcelFileDescriptor.class });
ctor.setAccessible(true);
UsbDeviceConnection conn = (UsbDeviceConnection)ctor.newInstance(new Object[] { devname, pfd });
Method open = UsbDevConnection.getMethod("open", new Class<?>[] { UsbDevice.class });
open.setAccessible(true);
Boolean result = (Boolean)open.invoke(new Object[] { conn, device });
pfd.close();
if(result)
return conn;
}
catch(Exception ex)
{
Log.i("USB_MANAGER", "Exception: " + ex.getMessage());
}
return null;
}
Invoking IUsbDevice.open() would throw an exception:
java.lang.IllegalArgumentException: device /dev/bus/usb/001/002 does not exist or is restricted
Putting that aside, chmod -R 777 /dev/bus/usb with root permissions does not work.
I've ran out of hacks I could find... As this is supposed to be a supported feature in ICS installed on USB Host capable device, I resort to (wanting to) ask the kernel author on this issue here.
Edit:
Native USB access works (UHC managed to mount my thumb drives)
Same problem here. Have you fixed this?
Hi,
I am coding an Android app using threads, but the start() function seems to not be recognized and reads: Cannot resolve symbol 'start'.
Here is my code:
Code:
Handler handler = new Handler();
Thread th = new Thread() {
public void run() {
// Asynctask
// delay
handler.postDelayed(this, 1000);
}
};
th.start();
I am new and don't know about this. I have android.os.Handler imported, but the start() is the only thing not working.
Thanks so much!
bump..?
Hello,
You should do
Code:
Thread th = new Thread(new Runnable(){
public void run() {
}
});
th.start();
But if you want a thread to execute periodically:
METHOD 1
Code:
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
handler.post(new Runnable() {
public void run() {
MyAsyncTask mAsync = new MyAsyncTask();
mAsync.execute();
}
});
}
};
timer.schedule(task, 0, 1000); //start with a delay of 0 ms and it executes every 1000ms (1 second)
and MyAsyncTask class is:
Code:
private class MyAsyncTask extends AsyncTask<String, Void, String> {
public MyAsyncTask(){
}
@Override
protected String doInBackground(String... params) {
//Background operation in a [B]separate[/B] thread
//Write here your code to run in the background thread
return null;
}
@Override
protected void onPostExecute(String result) {
//Called on Main UI Thread. Executed after the Background operation, allows you to have access to the UI
}
@Override
protected void onPreExecute() {
//Called on Main UI Thread. Executed before the Background operation, allows you to have access to the UI
}
@Override
protected void onProgressUpdate(Void... values) {
//Called on Main UI Thread
}
}
To wait for it to finish:
Code:
try{
if(myAsync!=null){
myAsync.get();
}
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}finally{
myAsync = null;
//Careful. We set null here. So do not reference myAsync again unless you create a new one (myAsync = new ...).
//If you want to reference it again without creating a new one, just delete this line along with the finally block
}
OR
To stop it immediately:
Code:
myAsync.cancel(true);
This stops the myAsync. Timer will still be creating and executing a new AsyncTask. To stop that:
Code:
timer.cancel(); // Terminates this timer, discarding any currently scheduled tasks.
timer.purge(); // Removes all cancelled tasks from this timer's task queue.
NOTE: If you execute the timer.cancel(), then you cannot use the same timer again. You need to create a new one like this:
Code:
timer = new Timer();
task = new TimerTask()..........
To learn more about AsyncTask, take a look here
METHOD 2
Code:
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
// do stuff
}
}, 0, 1, TimeUnit.SECONDS); //Start with a delay of 0 seconds and repeat every 1 second.
To stop the thread use:
Code:
exec.shutdown(); //It will not be executed any more, but the task that the thread is already doing will not be interrupted.
OR
Code:
exec.shutdownNow(); //It will interrupt the current work of the thread being running, and also prevents the thread from running periodically again.
If you have any further questions please let me know
i work with this guild: Android Uploading Camera Image, Video to Server with Progress Bar
and when i take image i got this error:
Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/Pictures/profile_imagephoto.jpg: open failed: ENOENT (No such file or directory
What can i do to fix this?
Code:
//Upload Profile Image
profileImage.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
pictureCheck = "to_profile";
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setIcon(R.drawable.ic_action_search);
builder.setMessage("Select What To Do:")
// Positive button functionality
.setPositiveButton("Camera",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int arg0) {
Toast.makeText(MainActivity.this, "Open Camera...", Toast.LENGTH_SHORT).show();
Intent cameraintent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraintent, IMAGE_CAPTURE);
}
})
// Negative button functionality
.setNegativeButton("Gallery",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int arg0) {
Toast.makeText(
MainActivity.this, "Open Gallery...", Toast.LENGTH_SHORT).show();
// Do more stuffs
Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
//dialog.cancel();
fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
galleryIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(galleryIntent, RESULT_LOAD_INAGE);
}
});
// Create the Alert Dialog
AlertDialog alertdialog = builder.create();
// Show Alert Dialog
alertdialog.show();
}
});
/**
* Here we store the file url as it will be null after returning from camera
* app
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// save file url in bundle as it will be null on screen orientation
// changes
outState.putParcelable("file_uri", fileUri);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// get the file url
fileUri = savedInstanceState.getParcelable("file_uri");
}
@Override
public void onActivityResult(int requestCodeToProfile, int resultCodeToProfile, Intent dataToProfile) {
super.onActivityResult(requestCodeToProfile, resultCodeToProfile, dataToProfile);
launchUploadActivity(true);
}
private void launchUploadActivity(boolean isImage){
Intent i = new Intent(MainActivity.this, UploadActivity.class);
i.putExtra("filePath", fileUri.getPath());
i.putExtra("isImage", isImage);
startActivity(i);
}
/**
* Creating file uri to store image/video
*/
public Uri getOutputMediaFileUri(int type) {
return Uri.fromFile(getOutputMediaFile(type));
}
/**
* returning image / video
*/
private static File getOutputMediaFile(int type) {
// External sdcard location
File mediaStorageDir =
new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"profile_image");
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d(TAG, "Oops! Failed create "
+ "profile_image" + " directory");
return null;
}
}
// Create a media file name
java.util.Date date= new java.util.Date();
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(date.getTime());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath() + "photo.jpg");
} else if (type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+ "VID_" + timeStamp + ".mp4");
} else {
return null;
}
return mediaFile;
}
This is a long shot, but the path of your image looks wrong. Are you missing a slash between the last dir and 'photo.jpg'. Should be this line:
mediaFile = new File(mediaStorageDir.getPath() + "photo.jpg");
Hey guys.
So, I've spent the last two days watching every tutorial about working with sqlite, but I'm doing something wrong and can't find out what.
The app is done, only needs the SQLite part, Its basicly a webapp with favorites.
Altough I have changed the code several times, this is what I ended up with:
DBManager.java
<code>
package com.rjpf.mywebapps;
import android.content.ContentValues;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
public class DBManager
{
private DbHelper dbHelper;
private Context context;
private SQLiteDatabase database;
public DBManager(Context c)
{
context=c;
}
public DBManager open() throws SQLException
{
dbHelper = new DbHelper(context);
database = dbHelper.getWritableDatabase();
return this;
}
public void close()
{
dbHelper.close();
}
public void insert(String name, String url)
{
ContentValues contentValue = new ContentValues();
contentValue.put(DbHelper.CONTACTS_COLUMN_NAME, name);
contentValue.put(DbHelper.CONTACTS_COLUMN_URL, url);
database.insert(dbHelper.CONTACTS_TABLE_NAME, null, contentValue);
}
}
</code>
When I try to call it in the main activity with:
MainActivity.java
<code>
(...)
private LinearLayout Layout_Add;
private TextView TxT_add_nomE;
private TextView TxT_add_urL;
private Button Button_Add_to_DB;
private Button btn_BACK_Add;
DbHelper myDB;
(...)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myDB = new DbHelper(this);
(...)
Button_Add_to_DB.setOnClickListener(new View.OnClickListener() {
@override
public void onClick(View view) {
Add_ItemToDb();
}
});
(...)
public void Add_ItemToDb()
{
if (TxT_add_nomE.getText().toString().trim().length() == 0 || TxT_add_urL.getText().toString().trim().length() == 0)
{
TxT_add_nomE.setText("Please don't leave fields empty!");
return;
}
else
{
String addNome = TxT_add_nomE.getText().toString();
String addUrl = TxT_add_urL.getText().toString();
// ADD TO DB
DBManager DataManager = new DBManager(this);
DataManager.insert(addNome,addUrl); // This line is what gives the error, when I click the button to add the App craches
TxT_add_nomE.setText("Name");
TxT_add_urL.setText("Url");
}
}
</code>
Thanks in advance and sorry for the long post, This is my first app and also the first time in java
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?