Android to android data transfer using usb - java

I have created an application which transfer data from android to android over wifi. I am exploring usb host apis to add support for data transfer over usb. I am following android docs for host apis
https://developer.android.com/guide/topics/connectivity/usb/host
and followed all the steps. So far I am able to grant permission to the selected device lets say phone "A" and opened usb device successfully to write data , but i don't know how to create/access usbdevice on other phone "B"? I have seen other phone isn't notified about the usb attached/unattached events. When I added "android.hardware.usb.action.USB_STATE" other side is notified by event change but I don't know how to get/access usbdevice object on phone "B" to initialize read/write data? Does the current host protocol is one sided protocol? Any help in this regard will be highly appreciated.

After reading further I was able to identify the problem. I had to force accessory mode in device from host, by sending special controlTransfer messages.
I found it here
https://github.com/peyo-hd/TcpDisplay/blob/master/sink/src/com/android/accessorydisplay/sink/SinkActivity.java
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_MANUFACTURER, MANUFACTURER);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_MODEL, MODEL);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_DESCRIPTION, DESCRIPTION);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_VERSION, VERSION);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_URI, URI);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_SERIAL, SERIAL);
// The device should re-enumerate as an accessory.
conn.controlTransfer(UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
UsbAccessoryConstants.ACCESSORY_START, 0, 0, null, 0, 10000);

Phone B is UsbDevice, it should use the USB Accessory function to connect with Accessory host(Phone A).
Phone B selects to accessory usb gadget function, which means Phone B's sys.usb.config = accessory. [getprop sys.usb.config => accessory]
App level design refer to AOA:
https://source.android.com/devices/accessories/protocol
https://developer.android.com/guide/topics/connectivity/usb/accessory.html#java
UsbAccessory accessory;
ParcelFileDescriptor fileDescriptor;
FileInputStream inputStream;
FileOutputStream outputStream;
...
private void openAccessory() {
Log.d(TAG, "openAccessory: " + accessory);
fileDescriptor = usbManager.openAccessory(accessory);
if (fileDescriptor != null) {
FileDescriptor fd = fileDescriptor.getFileDescriptor();
inputStream = new FileInputStream(fd);
outputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryThread");
thread.start();
}
}

Related

Why is GetExternalFileDirs not working in android?

I am working on a sample project in unity android where application reads a textfile in an OTG pen drive connected to the android phone, the problem is my code works fine in some devices but it doesn't work in some devices. I am unable to figure out the main cause even through the logcat extreme as it doesn't throw any error at all that specifies code error.
I tried adding text logs to print text in between the code to see which texts are not printed, which would further tell me that after which line code stopped working but all the texts are printed.
This is my code that detects the USB and returns true if a text file is present in USB:
public void LocateFile()
{
textfileData = null;
using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using (AndroidJavaObject context = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
{
// Get all available external file directories (emulated and USBdrives)
AndroidJavaObject[] externalFilesDirectories = context.Call<AndroidJavaObject[]>("getExternalFilesDirs", (object)null);
AndroidJavaObject emulated = null;
AndroidJavaObject USBdrive = null;
for (int i = 0; i < externalFilesDirectories.Length; i++)
{
AndroidJavaObject directory = externalFilesDirectories[i];
using (AndroidJavaClass environment = new AndroidJavaClass("android.os.Environment"))
{
bool isRemovable = environment.CallStatic<bool>("isExternalStorageRemovable", directory);
bool isEmulated = environment.CallStatic<bool>("isExternalStorageEmulated", directory);
if (isEmulated)
{
emulated = directory;
}
else if (isRemovable && isEmulated == false)
{
USBdrive = directory;
}
}
}
// Return the USBdrive if available
if (USBdrive != null)
{
if (File.Exists(USBdrive.Call<string>("getAbsolutePath") + "/serial.txt"))
{
textfileData = File.ReadAllText(USBdrive.Call<string>("getAbsolutePath") + "/serial.txt");
}
}
}
}}
This code works on my android phone and returns true after detecting the file present in pendrive.
Can anyone tell me what could be the issue and point me in the right direction, I will greatly appreciate your support on this.
This locateFile() function is called on Update().
Update: I tried to debug the number of storage found in mobile and code is not reading the USB. It reads internal and SD storage perfectly.
Nokia 5 is the only phone the code has worked on so far but in 99% android devices GetExternalFilesDir is not reading usb storage.
Please someone tell me an alternate or how do I get this to work, it is very important.

How to increase the sound Volume Output in Webrtc

Am working on a webrtc android application and everything is working fine except two things...
which are;
switching the default sound output device from the Earpiece to Speaker and vice versa..
I have tried to use the code below from this stackoverflow thread but they are not working
audioManager = (AudioManager) this.activity.getSystemService(Context.AUDIO_SERVICE);
audioManager.setSpeakerphoneOn(true);
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
And improving the video performance, there is a lot of lagging in the video streams where even the stream hangs a lot. if there is any one who can help me on this too thanks a lot.
Below is my peerConnection configs
String fieldTrials = (PeerConnectionFactory.VIDEO_FRAME_EMIT_TRIAL + "/" + PeerConnectionFactory.TRIAL_ENABLED + "/");
PeerConnectionFactory.InitializationOptions initializationOptions =
PeerConnectionFactory.InitializationOptions.builder(this)
.setFieldTrials(fieldTrials)
.createInitializationOptions();
PeerConnectionFactory.initialize(initializationOptions);
//Create a new PeerConnectionFactory instance - using Hardware encoder and decoder.
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(
rootEglBase.getEglBaseContext(), /* enableIntelVp8Encoder */true, /* enableH264HighProfile */true);
DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext());
peerConnectionFactory = PeerConnectionFactory.builder()
.setOptions(options)
.setVideoEncoderFactory(defaultVideoEncoderFactory)
.setVideoDecoderFactory(defaultVideoDecoderFactory)
.createPeerConnectionFactory();
Thanks
Need add this permission in manifest
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

Manual add song to Mediastore as a music track

I want to create a Music player which can download a song online and add it to MediaStore. I'm using Download Manager and allow MediaScanner scan this file when download completed.
DownloadManager.Request request ....
request.allowScanningByMediaScanner();
...
downloadManager.enqueue(request);
It's work fine in android 5.0 and above.
But the song was downloaded using codec (opus) which not supported in android below lollipop version, so MediaScanner doesn't add this file to MediaStore.
That's my problem, my app can play opus codec but the song didn't exist in MediaStore after it has downloaded, so my app can't find this song.
How to force MediaScanner add downloaded file to MediaStore.Audio as a Music track. If can not, how can I manual add this song to MediaStore.Audio after download completed:
public class BroadcastDownloadComplete extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.DOWNLOAD_COMPLETE")) {
//addSongToMediaStore(intent);
}
}
}
From the source code here, we can see the final implementation of the scanner has two steps to scan an audio file. If either of these two step fail, the audio file will not insert into media provider.
step 1 check the file extension
static bool FileHasAcceptableExtension(const char *extension) {
static const char *kValidExtensions[] = {
".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
".avi", ".mpeg", ".mpg"
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
for (size_t i = 0; i < kNumValidExtensions; ++i) {
if (!strcasecmp(extension, kValidExtensions[i])) {
return true;
}
}
return false;
}
More extensions have been added since Android 5.0. The common container for opus codec is ogg, this extension exists before Android 5.0. Assume your audio file extension is ogg, the scanning process is fine at this step.
step2 retrieve metadata
After the first step passed, the scanner need to retrieve media's metadata for later database insertion. I think the scanner do the codec level checking at this step.
sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);
int fd = open(path, O_RDONLY | O_LARGEFILE);
status_t status;
if (fd < 0) {
// couldn't open it locally, maybe the media server can?
status = mRetriever->setDataSource(path);
} else {
status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL);
close(fd);
}
if (status) {
return MEDIA_SCAN_RESULT_ERROR;
}
For Android version before 5.0, the scanner might be failed at this step. Because of lacking of built-in opus codec support, setDataSource will get failed at last. The media file won't be added to media provider finally.
suggested solution
Because we know the audio file will be added to
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
we can do database operation manually. If you want your audio file keeps consistent with other audio files in the database, you have to retrieve all the metadata by yourself. Since you can play the opus file, I think it's easy to retrieve the metadata.
// retrieve more metadata, duration etc.
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Audio.AudioColumns.DATA, "/mnt/sdcard/Music/example.opus");
contentValues.put(MediaStore.Audio.AudioColumns.TITLE, "Example track");
contentValues.put(MediaStore.Audio.AudioColumns.DISPLAY_NAME, "example");
// more columns should be filled from here
Uri uri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues);
Log.d(TAG, uri.toString());
After that, you app can find the audio file.
getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI...
You can use MediaScannerConnection to ask Android to scan a file to be included as media. You'll want to use the scanFile() static method.

Print pdf file through bluetooth printer

As new to the android bluetooth connecting and printing the content in printer.But now able to connect to bluetooth printer with my android device.So,with my project requirement i have to print the content of the pdf file.Previously with the same project i am able to print the string variable content.
Now with the project requirement change there will be a pdf file priniting task.First I am creating the pdf with the itext library in java for pdf file creation.So,the file creation part is done.And what i did for printing that file is generate the byte array for the pdf file.
here is the line of code
FileInputStream fin=new FileInputStream(pdffile);
fileContent=new byte[(int) pdffile.length()];//file content is the byte array for the pdf file.
Next line connecting to the bluetooth of the printer to the android device.
mBTAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice mdevice = mBTAdapter.getRemoteDevice(PRINTER_MAC_ID);
Method m = mdevice.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
mBTSocket = (BluetoothSocket) m.invoke(mdevice, 1);
mBTSocket.connect();
with that socket(mBTSocket) getting the OutputStream.
and than writing the byte array to that OutputStream.
os.write(fileContent);
os.flush();
mBTSocket.close();
so when i tried to print the pdf file content through the bluetooth printer,nothing happen there is no exception or application crash but it give warning getbluetoothservice() called with no bluetoothmanagercallback and not content print on the paper.So anyone can tell me which/where i m doing wrong.I also search for this topic but all thing i got about string printing only but no file.
One of the link tell about some sdk named StarIOsdk for android for printing file.And one more problem in the android sdk that the new printing methodlogy is introduced in API level 4.4 but how we will do in prior API level.The printer used here is bluetooth thermal printer(small size 2 inche paper size).Thanks in advance.
mBTAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice mdevice = mBTAdapter.getRemoteDevice(PRINTER_MAC_ID);
Method m = mdevice.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
mBTSocket = (BluetoothSocket) m.invoke(mdevice, 1);
mBTSocket.connect();
Thread.sleep(100);
After Socket Connect put thread to sleep ..This worked for me

Route MIC input through headset and AUDIO output through phone's speaker

is there a way to route microphone input through headset and use simultaneously the AUDIO output through smartphone's speaker ?
I've been watching around for hours now and I saw that it's clearly impossible on iOS but what about Android..
I'm using a Samsung Galaxy S4.
Here is a part of my code to route the audio output through the speaker even if the headset is plugged in :
AudioManager speakerOutput = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
speakerOutput.setMode(AudioManager.MODE_IN_CALL);
speakerOutput.setSpeakerphoneOn(true);
But then when I tried to control my app by the headset's microphone, nothing. So I tried with the smartphone's one and it's obviously working.
It don't seems like others AudioManager.MODEs route audio output only and leave the mic input to the headset. So what can I do now without having to build a new kernel, modify drivers, HAL, …etc ?
Thanks, C.
There is no way to loopback from headset Mic to Speaker if you do not change HAL or driver on Android. If u can change hardware, it possible works out.
If you use AudioManager.STREAM_RING, the audio should be routed to both the loudspeaker and the wired headset, just as when the ringtone is played for an incoming call (remove the setMode and setSpeakerphoneOn calls).
Yes, It's possible. I successfully do it a few days ago. There are 2 solutions for it:
Solution 1:
Suppose you are in a normal mood & Headset connected. We just need to call setPreferredDevice function to set your preferred audio input/output device. Our input source is already coming from the headset. So we only need to route the audio output source to the Phone Speaker. Here is my sample code by Media Player:
#RequiresApi(Build.VERSION_CODES.P)
private fun playAudioFromPhoneSpeaker(context: Context, audioResId: Int) {
val mMediaPlayer: MediaPlayer = MediaPlayer.create(context, audioResId)
val mCurrentDevice: AudioDeviceInfo? = mMediaPlayer.preferredDevice
val mDeviceSpeaker: AudioDeviceInfo? = getDeviceOutputSpeaker(context)
mDeviceSpeaker?.let { speaker ->
if (mCurrentDevice != speaker) {
val result: Boolean? = mMediaPlayer.setPreferredDevice(mDeviceSpeaker)
Log.i("PreferredDevice", "is set as speaker: $result")
}
}
mMediaPlayer.start()
mMediaPlayer.setOnCompletionListener { mediaPlayer ->
mediaPlayer.release()
}
}
#RequiresApi(Build.VERSION_CODES.M)
private fun getDeviceOutputSpeaker(context: Context): AudioDeviceInfo? {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
mAudioDeviceOutputList?.filterNotNull()?.forEach { speaker ->
if (speaker.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
return speaker
}
}
return null
}
The positive thing about this solution is that we don't need to think about the input source. We only change the audio output source and it doesn't have any impact on the input source.
The problem is setPreferredDevice of MediaPlayer come from Android 9. So this solution is only supported from Android 9.
Solution 2:
In 2nd solution we turn on device speaker by Audio Manager. So after turn on the device speaker the audio input & output source will come to the device internal speaker & device mic. Now we have to route the mic to the headset. Sample code:
private val mAudioRecord: AudioRecord? = null
private fun RecordAudioFromHeadphone(context: Context) {
//Step 1: Turn on Speaker
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.isSpeakerphoneOn = true
//Step 2: Init AudioRecorder TODO for you
//Step 3: Route mic to headset
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setPreferredInputMic(getWiredHeadPhoneMic(this))
}
}
#RequiresApi(Build.VERSION_CODES.M)
private fun setPreferredInputMic(mAudioDeviceInfo: AudioDeviceInfo?) {
val result: Boolean? = mAudioRecord?.setPreferredDevice(mAudioDeviceInfo)
}
#RequiresApi(Build.VERSION_CODES.M)
private fun getWiredHeadPhoneMic(context: Context): AudioDeviceInfo? {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
mAudioDeviceOutputList?.filterNotNull()?.forEach { device ->
if (device.type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
return device
}
}
return null
}
The positive think of this solution is supported from Android 6. And negative thing is it's little bit complex and you have to maintain the voice input source.
AudioRecorder, MediaRecorder, AudioTrack also support this kind of solution.
Thanks

Categories