Connect Android USB Accessory when screen is off - android

Application Info
I've got an application that is able to communicate with a USB accessory when the screen is on.
The first thing I do, after installing the app, is turn the screen on and then plug in the USB accessory. I get the message "Open when this USB accessory is connected?". Additionally, there is a checkbox that says "Use by default for this USB accessory". I click the checkbox and press OK.[1]
Now, whenever I plug in the USB accessory, my application pops to the foreground. The reason this happens is because I have the following in my AndroidManifest.xml:
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="#xml/accessory_filter" />
After this happens, I then call enable() that is defined here:
public boolean enable() {
UsbManager deviceManager = null;
UsbAccessory[] accessories = null;
UsbAccessory accessory = null;
deviceManager = (UsbManager) context
.getSystemService(Context.USB_SERVICE);
accessories = deviceManager.getAccessoryList();
accessory = accessories[0];
if (accessory == null) {
return false;
}
logDRemote("Accessory is " + accessory);
// If we have permission to access the accessory,
if (deviceManager.hasPermission(accessory)) {
parcelFileDescriptor = deviceManager
.openAccessory(accessory);
if (parcelFileDescriptor != null) {
logDRemote("descriptor is not null");
if(parcelFileDescriptor == null) {
logDRemote("parcelFileDescriptor == null");
return false;
}
outputStream = new FileOutputStream(
parcelFileDescriptor.getFileDescriptor());
if(outputStream == null) {
logDRemote( "outputStream == null");
return false;
}
logDRemote("outputStream open");
byte[] data = new byte[2];
data[0] = (byte) APP_CONNECT;
data[1] = 0;
try {
outputStream.write(data);
} catch (IOException e) {
logDRemote("Could not open accessory");
closeAccessory();
return RETURN_CODES.FILE_DESCRIPTOR_WOULD_NOT_OPEN;
}
return true;
} else {
return false;
}
} else {
return false;
}
}
When I call this after the activity pops to the foreground, I call this function and it returns true, showing that the USB device is properly communicating.
Failure with swipe up screen
The problem is, if I have my screen off without a PIN but I have to swipe up to unlock the phone, the following happens:
When I plug in the USB accessory, the screen turns on and says "swipe up"
My activity does not pop up until after I swipe up on the screen
If I never swipe on on the screen and try calling enable(), I get a failure at "Could not open accessory"
That is, the device is able to open properly, and the permissions exist. But it just can't actually write to the accessory when it tries.
Fix for swipe up screen failure
I found a way to fix this first issue. What I was able to do is open up the activity whenever I detect that a USB charger is present[3]. Now the following happens:
I start by installing the application with the USB unplugged
I turn off the screen
I plug in the USB device
The activity pops up
A second activity pops up. I believe that second activity popping up is the Android system opening the popup as a result of the USB attach event[2]
I call enable(), and the write succeeds.
Failure with pin lock
However, this fix does not work when I have a pin set on the phone. Now the following happens:
I start by installing the application with the USB unplugged.
I turn off the screen
I plug in the USB device
The activity pops up
A second activity does not pop up
I try calling enable(), and the write fails.
The Final Question
How do I connect to a USB device when my Android phone is off and has a pin set? I have root, if that helps.
Footnotes
As an aside, this writes to /data/system/users/0/usb_device_manager.xml, as seen in the source here: http://androidxref.com/7.0.0_r1/xref/frameworks/base/services/usb/java/com/android/server/usb/UsbSettingsManager.java#mSettingsFile
http://androidxref.com/7.0.0_r1/xref/frameworks/base/services/usb/java/com/android/server/usb/UsbSettingsManager.java#775
I'm putting the following in onCreate of the activity, so that it can show up while the screen is off:
-
final Window win= getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

Related

How to get the bluetooth a2dp connetcted event on xamarin?

I have a xamarin project (API 28, soon to be 29), and I need to catch the event of bluetooth a2dp device.
I have a broadcast receiver with the following intent filter:
IntentFilter bluetoothFilter = new IntentFilter();
bluetoothFilter.AddAction(BluetoothDevice.ActionAclConnected);
bluetoothFilter.AddAction(BluetoothDevice.ActionAclDisconnectRequested);
bluetoothFilter.AddAction(BluetoothDevice.ActionAclDisconnected);
var btReciever = new BluetoothReceiver();
this.RegisterReceiver(btReciever, bluetoothFilter);
In my manisfest, I got the following permission:
<uses-permission android:name="android.permission.BLUETOOTH" />
In the Receiver.OnReceive, I got this code:
public override void OnReceive(Context context, Intent intent) {
String action = intent.Action;
BluetoothDevice device = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
There I have a switch:
switch (action)
{
case BluetoothDevice.ActionFound:
Android.Util.Log.Debug(TAG, "Device Found");
//Device found
break;
case BluetoothDevice.ActionAclConnected:
Android.Util.Log.Debug(TAG, "Device Connected");
BluetoothAdapter.DefaultAdapter.GetProfileProxy(context, btsListener, ProfileType.A2dp);
Thread.Sleep(1500);
var manager = context.GetSystemService(Context.AudioService) as AudioManager;
var devices = manager.GetDevices(GetDevicesTargets.Outputs);
Android.Util.Log.Debug(TAG, "devices=" + devices.Length);
foreach (var dev in devices)
{
Android.Util.Log.Debug(TAG, "dev: id={0} name={1} type={2}", dev.Id, dev.ProductName, dev.Type);
if (dev.ProductName == device.Name)
{
if (dev.Type.ToString().Contains("a2dp"))
{
BluetoothAdapter.DefaultAdapter.GetProfileProxy(context, btsListener, ProfileType.A2dp);
}
Android.Util.Log.Debug(TAG, "Found output device");
}
}
//Device is now connected
break;
...
}
And I got a listener that implements the IBluetoothProfileServiceListener interface, and looks like this:
var btsListener = new BTServiceListener();
class BTServiceListener : AppCompatActivity, IBluetoothProfileServiceListener
{
public void OnServiceConnected([GeneratedEnum] ProfileType profile, IBluetoothProfile proxy)
{
if (profile == ProfileType.A2dp)
{
Android.Util.Log.Debug(TAG, "A2dp");
}
}
...
}
I need to catch the event onConnect of the bluetooth a2dp (and later headset), but I have no idea how exactly I should do it.
This code in the receiver, shows the bluetooth onConnect event (BluetoothDevice.ActionAclConnected in the switch), then I check the device list, there is not yet the connected device, then I wait 1500ms (I need somehow to improve this method, this cannot stay like this), for the audioService to add the actual a2dp device to the list, and in the for loop, I find the additional device via its name, and I am certain it is the right one. BUT, I have no programmaticaly way to find out what type of device was connected (remote, headset, a2bp...) other than to check is the name contains a2dp (see for loop)
After my research, I found this line:
BluetoothAdapter.DefaultAdapter.GetProfileProxy(context, btsListener, ProfileType.A2dp);
This uses the context, the listener, and the desierd type of device (see Listener: BTServiceListener ), the problem is, I don't know if the proxy in the listener is the same device as the device in the broadcast receiver onConnect, and I have no idea how to use that function.
So my questions:
How and when should I use the BluetoothAdapter.DefaultAdapter.GetProfileProxy function and be certain that I have the same device in the listener and in the onConnect function?
How to get all the devices from the manager without putting the thread to sleep? Because, without the Thread.Sleep, the actual device is not in the list because the onConnect function is called earlyer then the addition of the device to the audioService.
Thread.Sleep(1500); // <-- this needs to go
var manager = context.GetSystemService(Context.AudioService) as AudioManager;
var devices = manager.GetDevices(GetDevicesTargets.Outputs);
How should I distinguish between the device types? Because, I have a feeling that my method of String.Contains(string) is not the way to go
Sorry for the long question, thank you for your help and time.
Let me know, if you need anything else.

Under USB-hub the USB_DEVICE_DETACHED broadcast event fired not on all detaching devices

I write an app which uses three hardware USB-devices.
When I operate with any single device - both events (USB_DEVICE_ATTACHED/USB_DEVICE_DETACHED) fires normally. Not matter how I bind the Receiver - under the manual registration in the code or in the manifest. And not matter how this single device attached - under the HUB or directly to the device.
The problem is: when I connect three device all in one time (by connecting the filled HUB), USB_DEVICE_ATTACHED event fired for all three devices - I receive notification via onNewIntent callback (or standalone Receiver binded via manifest), and do the permission-approve trip.
But on physical removal all devices behind the HUB (by detaching the HUB itself) DETACH event fires not on all devices. It may fired on random single device, or random two devices. But never on all three devices.
As result, for the time UsbManager holds a lot of dead dev-handles of not physically connected devices. At the same time, when I look into /dev/usb/ folder in connecion/disconnection time, device-files appears and removes correctly. As next result, when app is restarted by the some reason, I can not recognize which device is dead and which device is physically present.
The device is ASUS P1801 tablet, OS 4.2.1
As I can see, it is the last firmware from asus (JOP40D.US_epad-10.6.1.22.5-20130715)
For caching purposes, handles of the wrapped-device I pin to the Application singleton (when device appears with the USB_DEVICE_ATTACHED event or app initialisation):
public class HWMountReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (Application.getAppContext().isAppReady() && (Application.getAppContext() != null) && (Application.getAppContext().getUsbHWManager() != null)) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Application.getAppContext().getUsbHWManager().refreshDevice(device);
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(Application.getAppContext(), 0, new Intent(Application.MED_ONLINE_DEVICE_PERMISSION), 0);
((UsbManager) Application.getAppContext().getSystemService(Context.USB_SERVICE)).requestPermission(device, mPermissionIntent);
}
}
}
The singleton:
private static FirstDevice firstDevice;
private static SecondDevice secondDevice;
private static ThirdDevice thirdDevice;
public void refreshDevice(UsbDevice device) {
HWDevice dev = HWDevicesFactory.newInstance(device);
if (dev instanceof FirstDevice) {
firstDevice = (FirstDevice) dev;
} else if (dev instanceof SecondDevice) {
secondDevice = (SecondDevice) dev;
} else if (dev instanceof ThirdDevice) {
thirdDevice = (ThirdDevice) dev;
}
}
Factory do reInstantination of each device-wrapper:
public static HWDevice newInstance(final UsbDevice device) {
int vendorId = device.getVendorId();
int productId = device.getProductId();
if ((vendorId == FIRST_VENDOR_ID) && (productId == FIRST_PRODUCT_ID)) {
return new FirstDevice(device);
...
Device-Wrapper more specific, because each one is very different. But typicaly it contain cached handles on UsbInterface, UsbDeviceConnection, UsbEndpoints and finally UsbDevice itself.
The main thing - DETACH not fired on random device. Not matter how it was "wrapped" by mine
Is there a probably bug in the ASUS firmware,
-or-
I miss some necessary steps/checks with the device connection
-or-
?

programmatically close Android settings after event has occured

So I created an app that connects to a wireless display on Android automatically for the user. The easiest way I found to do that is by opening the Screen Mirroring settings. This makes it easier for the user, so they don't have to go up to settings and enable it themselves.
Now that I have connected to the display, I want the Screen Mirroring screen to go away and return to the app or to the home screen if the user wants.
Here is the code I use to open Screen Mirroring settings to connect the user to the display after he clicks on a button:
try
{
activityint = 1;
Log.d("DEBUG", "open WiFi display settings in HTC");
startActivityForResult(new Intent("com.htc.wifidisplay.CONFIGURE_MODE_NORMAL"),activityint);
} catch (Exception e)
{
try
{
activityint = 2;
Log.d("DEBUG", "open WiFi display settings in Samsung");
startActivityForResult(new Intent("com.samsung.wfd.LAUNCH_WFD_PICKER_DLG"),activityint);
}catch (Exception e2)
{
activityint=3;
Log.d("DEBUG", "open WiFi display settings in stock Android");
startActivityForResult(new Intent("android.settings.WIFI_DISPLAY_SETTINGS"),activityint);
}
}
And then I have a broadcastreceiver that listens for WIFI_P2P_CONNECTION_CHANGED_ACTION. When this happens, it will look to see if we are now connected before launching other things and then attempting to close the settings activity.
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION))
{
/**
* What to do if the P2P connection has changed
*/
try
{
NetworkInfo info = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if(info!=null && info.isConnected())
{
connected(true);
//Kill the settings activity
finishActivity(activityint);
}else if(info!=null && !info.isConnected())
{
connected(false);
}
}catch(Exception e)
{
Log.e("DEBUG", "exception", e);
}
}
}
The problem is that it kills the settings activity before the connection is finalized. So it will back out of the settings activity and the Screen Mirroring connection will cancel a moment before it connects. Is there a better way or a different way to be able to back out of the Settings activity? Or am I listening for the wrong intent in my receiver?
Thanks
The only way I was able to close a settings page on some event, is to start my activity again with the Intent.FLAG_ACTIVITY_CLEAR_TOP flag.
This way, because your activity is the one that opened the settings activity, the flag will make the system finish it. To the user it will appear that the settings activity just went away.
I guess I'm answering my own questions. I found this code that allows me to send a delayed response. I know this is crummy programming because some devices will finish before others, but this works for me at the moment. If anyone knows of a better way, please feel free to let me know.
This goes in my broadcast receiver's onReceive method.
NetworkInfo info = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if(info!=null && info.isConnected())
{
connected(true);
new Handler().postDelayed(new Runnable()
{
public void run()
{
finishActivity(activityint);
}
}, 5000);
}else if(info!=null && !info.isConnected())
{
connected(false);
}
EDIT: This actually does not work for me. After testing further, I found that it will kick me back to the home screen after a few moments even when I just open the app. It's pretty annoying. Anyone know of a better idea?

Android - disable HDMI

One of my Android projects needs to switch between 2 HDMI inputs from time to time, possibly once a minute. One HDMI input is from an android device's HDMI output and one is from an external, uncontrollable source.
I found an HDMI switch that automatically switches between 2 sources when the signal becomes available.
My question is, is there a way to temporarily (one minute for example) cut HDMI output of my Android device so that the switch can automatically use the second HDMI input? Then, I need to restore the HDMI output so that the switch will show my device's HDMI output.
I found this question but I am not sure I need to disable HDMI output but rather redirect the display in some way and restore it back after 1 minute.
UPDATE
I want to start a bounty so I will clarify my request a bit: I have an HDMI-enabled TV with 2 ports. My android device is connected on port 1, another device is connected on port 2. The TV automatically switches to the next HDMI port that has signal.
For example, if HDMI1 and HDMI2 have signals, I put my TV on HDMI1. When the first device "closes" its HDMI output, the TV will switch to HDMI2. After a while (5 minutes) the first devices "re-opens" HDMI1 (meaning the first device enables its HDMI output) and the second device "closes" its HDMI output so the TV will switch back to HDMI1. This way I can make a mix of videos.
The technical difficulty I am facing is how to control the HDMI output in Android systems. My Android device only has HDMI interface for displaying, it doesn't have a dedicated screen.
The only stuff close enough to what I need is this SO post, but it doesn't really help in my case.
The best approach is to use to use the set of commands related to DisplayID, that allows you to listen for displays to be added, changed or removed.
Here is a quick example how it goes to change your display/HDMI:
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
#Override
public void onDisplayAdded(int displayId) {
Log.d(TAG, "Display #" + displayId + " added.");
mDisplayListAdapter.updateContents();
}
#Override
public void onDisplayChanged(int displayId) {
Log.d(TAG, "Display #" + displayId + " changed.");
mDisplayListAdapter.updateContents();
}
#Override
public void onDisplayRemoved(int displayId) {
Log.d(TAG, "Display #" + displayId + " removed.");
mDisplayListAdapter.updateContents();
}
};
And here how to get all your HDMI/display devices available to connect:
protected void onResume() {
// Be sure to call the super class.
super.onResume();
// Update our list of displays on resume.
mDisplayListAdapter.updateContents();
// Restore presentations from before the activity was paused.
final int numDisplays = mDisplayListAdapter.getCount();
for (int i = 0; i < numDisplays; i++) {
final Display display = mDisplayListAdapter.getItem(i);
final PresentationContents contents =
mSavedPresentationContents.get(display.getDisplayId());
if (contents != null) {
showPresentation(display, contents);
}
}
mSavedPresentationContents.clear();
// Register to receive events from the display manager.
mDisplayManager.registerDisplayListener(mDisplayListener, null);
}
To unregister you use:
//unregisterDisplayListener(DisplayManager.DisplayListener);
#Override
protected void onPause() {
// Be sure to call the super class.
super.onPause();
// Unregister from the display manager.
mDisplayManager.unregisterDisplayListener(mDisplayListener);
// Dismiss all of our presentations but remember their contents.
Log.d(TAG, "Activity is being paused. Dismissing all active presentation.");
for (int i = 0; i < mActivePresentations.size(); i++) {
DemoPresentation presentation = mActivePresentations.valueAt(i);
int displayId = mActivePresentations.keyAt(i);
mSavedPresentationContents.put(displayId, presentation.mContents);
presentation.dismiss();
}
mActivePresentations.clear();
}
About "invalidate" HDMI output, if it eventually occurs, just redraw it. This should solve any "invalidate", in case it happens.
You might find useful to check further documentation.

When exactly is the NFC Service deactivated?

I am wondering when exactly the NFC Service is started and stopped.
The source code for android 4.0.3 seems to state that the polling is dependent on a single constant (located in NfcService.java)
/** minimum screen state that enables NFC polling (discovery) */
static final int POLLING_MODE = SCREEN_STATE_ON_UNLOCKED;
I would interpret this as "the screen light is on, therefore the nfc service is active".
BUT when the screen is locked, a NFC Tag wont be recognized, altough the screen is lit.
So I am curious: Is the NFC Service already deactivated when the lock screen appears, or is it still running but not processing the Tags?
Actually, I do not think that NFC Service is deactivated. When the screen has lower value then SCREEN_STATE_ON_UNLOCKED a device stops to ask NFC tags around. You can see this from this code:
// configure NFC-C polling
if (mScreenState >= POLLING_MODE) {
if (force || !mNfcPollingEnabled) {
Log.d(TAG, "NFC-C ON");
mNfcPollingEnabled = true;
mDeviceHost.enableDiscovery();
}
} else {
if (force || mNfcPollingEnabled) {
Log.d(TAG, "NFC-C OFF");
mNfcPollingEnabled = false;
mDeviceHost.disableDiscovery();
}
}
But NFC-EE routing is enabled util screen state is higher then SCREEN_STATE_ON_LOCKED:
// configure NFC-EE routing
if (mScreenState >= SCREEN_STATE_ON_LOCKED &&
mEeRoutingState == ROUTE_ON_WHEN_SCREEN_ON) {
if (force || !mNfceeRouteEnabled) {
Log.d(TAG, "NFC-EE ON");
mNfceeRouteEnabled = true;
mDeviceHost.doSelectSecureElement();
}
} else {
if (force || mNfceeRouteEnabled) {
Log.d(TAG, "NFC-EE OFF");
mNfceeRouteEnabled = false;
mDeviceHost.doDeselectSecureElement();
}
}
The service itself is started and stopped in other parts of this class.
See related http://forum.xda-developers.com/showthread.php?t=1712024&page=14

Categories

Resources