Skip to content

Commit

Permalink
Merge pull request #8 from rareranger/usb_mount_test
Browse files Browse the repository at this point in the history
Add USB mount options for rooted devices
  • Loading branch information
rareranger authored Jan 17, 2022
2 parents a85839e + 3fb47fd commit 86596ad
Show file tree
Hide file tree
Showing 6 changed files with 450 additions and 1 deletion.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ dependencies {

implementation 'com.android.support:multidex:1.0.3'
implementation 'com.nanohttpd:nanohttpd-webserver:2.1.0'
implementation 'eu.chainfire:libsuperuser:1.1.0.+'
}
Binary file added app/src/main/assets/exfathax.img
Binary file not shown.
57 changes: 56 additions & 1 deletion app/src/main/java/org/ktech/ps4jailbreak/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.net.wifi.WifiManager
import android.os.Build
import android.os.Bundle
import android.provider.DocumentsContract
import android.text.format.Formatter.formatIpAddress
Expand All @@ -15,11 +16,13 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.*
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File
import java.io.InputStream
import java.lang.Exception
import java.net.Socket
Expand All @@ -28,6 +31,7 @@ import kotlin.collections.ArrayList
import kotlin.system.exitProcess

const val PICK_BIN_FILE = 2
const val IMG_FILENAME = "exfathax.img"

class MainActivity : AppCompatActivity(), AdapterView.OnItemSelectedListener {

Expand All @@ -51,6 +55,9 @@ class MainActivity : AppCompatActivity(), AdapterView.OnItemSelectedListener {
//Listener for changing preferences
lateinit var listener: SharedPreferences.OnSharedPreferenceChangeListener

lateinit var usbMounter: USBMounter

@RequiresApi(Build.VERSION_CODES.KITKAT)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Expand Down Expand Up @@ -137,8 +144,56 @@ class MainActivity : AppCompatActivity(), AdapterView.OnItemSelectedListener {
showToast("Add and choose a payload to send!")
}
}

//Initialize USBMounter class
usbMounter = USBMounter()
//Log on event
usbMounter.onLogMessage = {
log(it)
}

//Setup event for when mount button is clicked
findViewById<Button>(R.id.btnMountUSB).setOnClickListener {
val imgFileName: String = getFileFromAssets(this, IMG_FILENAME).absolutePath
GlobalScope.launch {
//Attempt to mount image
usbMounter.mountImage(imageFile = imgFileName, toast = this@MainActivity::showToast)
runOnUiThread {
if (usbMounter.backendType == USBMounter.BACKEND_TYPE.NA) {
findViewById<Button>(R.id.btnMountUSB).isEnabled = false
findViewById<Button>(R.id.btnUnmountUSB).isEnabled = false
}
}
}
}

findViewById<Button>(R.id.btnUnmountUSB).setOnClickListener {
GlobalScope.launch {
//Attempt to unmount image
usbMounter.unmount_image(toast = this@MainActivity::showToast)
runOnUiThread {
if (usbMounter.backendType == USBMounter.BACKEND_TYPE.NA) {
findViewById<Button>(R.id.btnMountUSB).isEnabled = false
findViewById<Button>(R.id.btnUnmountUSB).isEnabled = false
}
}
}
}

}

fun getFileFromAssets(context: Context, fileName: String): File = File(context.cacheDir, fileName)
.also {
if (!it.exists()) {
it.outputStream().use { cache ->
context.assets.open(fileName).use { inputStream ->
inputStream.copyTo(cache)
inputStream.close()
}
}
}
}

//Update spinner items
private fun updateSpinnerItems() {
items = ArrayList()
Expand Down Expand Up @@ -178,7 +233,7 @@ class MainActivity : AppCompatActivity(), AdapterView.OnItemSelectedListener {
}

//Custom function for showing toast message
private fun showToast(msg: String, long: Boolean = false) {
public fun showToast(msg: String, long: Boolean = false) {
runOnUiThread {
if (long) {
Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
Expand Down
160 changes: 160 additions & 0 deletions app/src/main/java/org/ktech/ps4jailbreak/PathResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package org.ktech.ps4jailbreak;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.widget.Toast;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

import androidx.annotation.RequiresApi;

import java.lang.reflect.Method;
import java.lang.reflect.Array;

public class PathResolver {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static String getPath(Context context, Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];

if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
} else {
// Below logic is how External Storage provider build URI for documents
// Based on http://stackoverflow.com/questions/28605278/android-5-sd-card-label and https://gist.github.com/prasad321/9852037
StorageManager mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);

try {
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getUuid = storageVolumeClazz.getMethod("getUuid");
Method getState = storageVolumeClazz.getMethod("getState");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
Method isEmulated = storageVolumeClazz.getMethod("isEmulated");

Object result = getVolumeList.invoke(mStorageManager);

final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
//String uuid = (String) getUuid.invoke(storageVolumeElement);

final boolean mounted = Environment.MEDIA_MOUNTED.equals(getState.invoke(storageVolumeElement))
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(getState.invoke(storageVolumeElement));

//if the media is not mounted, we need not get the volume details
if (!mounted) continue;

//Primary storage is already handled.
if ((Boolean)isPrimary.invoke(storageVolumeElement) && (Boolean)isEmulated.invoke(storageVolumeElement)) continue;

String uuid = (String) getUuid.invoke(storageVolumeElement);

if (uuid != null && uuid.equals(type))
{
String res =getPath.invoke(storageVolumeElement) + "/" +split[1];
return res;
}
}
}
catch (Exception ex) {
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {split[1]};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}

public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}

public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}

/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
Loading

0 comments on commit 86596ad

Please sign in to comment.