BroadcastReceiver monitoring for a specific USB device attached - android

I have an activity that starts when a specific USB device is attached, as specified in a device filter file, which works great:
<activity
android:name="com.mycompany.DerpApp.MainActivity"
android:launchMode="singleTask"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/device_filter" />
</activity>
I also have a service where I want to monitor for connect and disconnect. I have BroadcastReceivers wired up, and they fire upon device attach and detach. However, I want these broadcast receivers to trigger when only a device as specified in my device_filter.xml is attached/detached.
m_usbDisconnectReceiver = new UsbDisconnectReceiver();
registerReceiver(m_usbDisconnectReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
m_usbConnectReceiver = new UsbConnectReceiver();
registerReceiver(m_usbConnectReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED));
I am unsure of how to attach my device_filter file to the programatically created Broadcast Receiver. Is there something I can do in the IntentFilter for this? I think that the Intent that is provided to the onReceive() has a UsbDevice as one of its extras, but it would be best if I could filter it out so the event doesn't fire. And if that's not possible, how can I check to see if the UsbDevice is a part of my device_filter?

If you absolutely have to create your BroadcastReceiver programatically, then you need to filter the devices programatically as well.
Try this:
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED))
{
UsbDevice d = (UsbDevice)
intent.getExtras().get(UsbManager.EXTRA_DEVICE);
if (d.getVendorId() == MY_VENDOR_ID && d.getDeviceId() == MY_DEVICE_ID)
{
// Your code here
}
}
}

Related

Hide NFC app from "Choose Application" list / Disable start by external NFC intent

I'm currently writing a couple of NFC enabled apps for Android and wanted to know how I can stop my application from being in the "choose application" list that's opened whenever a tag is scanned from the launcher or a non-NFC app. I only want my apps to be able to read the tags when they are open.
My current intent filters:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="#xml/nfc_tech_filter" />
nfc_tech_filter.xml:
<tech-list>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.Ndef</tech>
</tech-list>
You currently have a couple of NFC related intent filters in your app's manifest:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
By adding such intent filters to the manifest of your app, you indicate that certain activities should be started (or presented as options in the intent chooser if multiple activities are registered for the same intents) upon these NFC tag discovery events.
Consequently, you need to remove those intent filters from the AndroidManifest.xml if you don't want your app to be launchable by those intents.
However, you would then also lose the ability to listen for those intents while an activity of your app is in the foreground. Since NFC intents are activity intents, you can't receive them through dynamically registered broadcast receivers (cf. RubbelDieKatz's answer). Instead, the Android API provides alternative means to catch NFC related events while an activity of your app is displayed in the foreground (this also allows your app to get precedence over other apps, which is not possible with manifest-based NFC intent filters):
Since API level 10 (basically since the beginning of Android NFC), Android features the foreground dispatch system. You can register your foreground activity to receive NFC events by using the method NfcAdapter.enableForegroundDispatch() upon onResume() (don't forget to unregister when your activity loses focus in onPause()):
public void onResume() {
super.onResume();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
adapter.enableForegroundDispatch(this, pendingIntent, null, null);
}
public void onPause() {
super.onPause();
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
adapter.disableForegroundDispatch(this);
}
You could even register to receive only a specific subset of NFC discovery events by providing the optional tech-list and intent-filters arguments to enableForegroundDispatch().
Once you registered your activity with the foreground dispatch system, you will receive NFC events through the onNewIntent() callback:
public void onNewIntent(Intent intent) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
...
}
Starting with API level 19, there is the new NFC reader mode API. You can now register to receive regular callbacks (instead of going through the intent dispatch which sometimes causes soome hickups). You can do this by using the method NfcAdpater.enableReaderMode():
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
adapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A, null);
}
By using other flags in addition to (or instead of) NfcAdapter.FLAG_READER_NFC_A you could set to reader mode to listen for other tag technologies as well.
You will then receive callbacks upon tag discovery (the receiver needs to implement the callback interface NfcAdapter.ReaderCallback):
public void onTagDiscovered(Tag tag) {
...
}
Also see What's the difference between enableReaderMode and enableForegroundDispatch? for a more detailed comparison between the two options.
You're using the NFC and Tech Discovered intent-filters. You could remove them and register intent listeners programmatically in your app.
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
About registering the intent: Here's a very good answer.
In your onCreate method you can register a receiver like this:
private BroadcastReceiver receiver;
#Override
public void onCreate(Bundle savedInstanceState){
// your oncreate code should be
IntentFilter filter = new IntentFilter();
filter.addAction("SOME_ACTION");
filter.addAction("SOME_OTHER_ACTION");
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//do something based on the intent's action
}
};
registerReceiver(receiver, filter);
}
Remember to run this in the onDestroy method:
#Override
protected void onDestroy() {
if (receiver != null) {
unregisterReceiver(receiver);
receiver = null;
}
super.onDestroy();
}
Another interesting quote from the answer below that one:
One important point that people forget to mention is the life time of
the Broadcast Receiver. The difference of programmatically
registering it from registering in AndroidManifest.xml is that.
In the manifest file, it doesn't depend on application life time.
While when programmatically registering it it does depend on the
application life time. This means that if you register in
AndroidManifest.xml, you can catch the broadcasted intents even when your application is not running.
Edit: The mentioned note is no longer true as of Android 3.1, the Android system excludes all receiver from receiving intents by default
if the corresponding application has never been started by the user or
if the user explicitly stopped the application via the Android menu
(in Manage → Application).
https://developer.android.com/about/versions/android-3.1.html
This is an additional security feature as the user can be sure that
only the applications he started will receive broadcast intents.
So it can be understood as receivers programmatically registered in
Application's onCreate() would have same effect with ones declared
in AndroidManifest.xml from Android 3.1 above.
If you unregister your receivers when your activity is killed, your application won't receive these broadcasts anymore.

USB Permissions without prompt

I have 2 activities within my app. Say FirstActivity and SecondActivity. FirstActivity is the MAIN and LAUNCHER activity. SecondActivity uses usb devices. I want the prompt for USB permission to appear only once within lifetime of the app.
If there was only one Activity, the following lines in manifest solved my purpose:
<activity android:name=".FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/usb_device_filter" />
</activity>
This was doing the following:
Launching FirstActivity whenever usb device (mentioned in xml) is connected is app is not already open.
Prompt for usb device permission appears only once.
How do I modify this to achieve the following:
If SecondActivity is already running and a new usb device is attached, I must be able to use the device without relaunching the app. So I have written a broadcast receiver for the same as follows:
public class TriggerReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
read connected usb devices and register serial port call listener back.
}
}
But the problem is FirstActivity gets relaunched again when usb device is attached while SecondActivity is running. How do I avoid this ?
Will add more information if needed. Would be thankful for any help.
Try "remove" intent filter <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> from FirstActivity like in this question
Update
FirstActivity triggers on every USB_DEVICE_ATTACHED (even SecondActivity is running) because You set <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> filter for it in AndroidManifest.xml file. So You should disable this filter when SecondActivity running. You can do this by that way:
1) add (e.g. AliasFirstActivity) in AndroidManifest.xml to your FirstActivity and move <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> filter to alias description (You should remove <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />from FirstActivity description) like that:
<activity-alias
android:targetActivity=".FirstActivity"
android:name=".AliasFirstActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/device_filter" />
</activity-alias>
2) add this code to onCreate() (or to onResume()) of SecondActivity
PackageManager pm = getApplicationContext().getPackageManager();
ComponentName compName =
new ComponentName(getPackageName(), getPackageName() + ".AliasFirstActivity");
pm.setComponentEnabledSetting(
compName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
to suppress intent filter USB_DEVICE_ATTACHED for FirstActivity. You should have in SecondActivity something like that:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
triggerReceiver = new TriggerReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
registerReceiver(triggerReceiver, filter);
PackageManager pm = getApplicationContext().getPackageManager();
ComponentName compName =
new ComponentName(getPackageName(), getPackageName() + ".AliasFirstActivity");
pm.setComponentEnabledSetting(
compName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
That should solve Your issue.
3) if it is needed, You can restore <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> filter for FirstActivity in onDestroy() (or in onPause()) of SecondActivity by using this code:
PackageManager pm = getApplicationContext().getPackageManager();
ComponentName compName =
new ComponentName(getPackageName(), getPackageName() + ".AliasFirstActivity");
pm.setComponentEnabledSetting(
compName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
But the problem is FirstActivity gets relaunched again when usb device is attached while SecondActivity is running. How do I avoid this?
This is not hard to answer. In your AndroidManifest.xml you literally declare that your FirstActivity should be launched when the event android.hardware.usb.action.USB_DEVICE_ATTACHED occurs.
If you want to handle this event in SecondActivity only, then you have to declare it in the manifest accordingly, for example:
<activity android:name=".FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/device_filter" />
</activity>
Now, whenever your USB device is plugged in, SecondActivity will be launched only. If SecondActivity is already running, then it won't be launched again (or multiple times) because of the attribute android:launchMode="singleTask" specified for SecondActivity. You can read more about the different launch modes here if you're interested.
Since you declared in your manifest that SecondActivity should be launched when a USB device is plugged in, the Android system will ask you the following question:
After ticking the checkbox "Use by default for this USB device", it won't ask you again. Now, everytime you plug in the USB device, your SecondActivity will be launched and it will automatically receive the required USB permissions too.
Let me know if that answers your question.
This answer might be helpful:
USB device access pop-up suppression?
Code snippet:
public class UsbEventReceiverActivity extends Activity
{
public static final String ACTION_USB_DEVICE_ATTACHED = "com.example.ACTION_USB_DEVICE_ATTACHED";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
protected void onResume()
{
super.onResume();
Intent intent = getIntent();
if (intent != null)
{
if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED))
{
Parcelable usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
// Create a new intent and put the usb device in as an extra
Intent broadcastIntent = new Intent(ACTION_USB_DEVICE_ATTACHED);
broadcastIntent.putExtra(UsbManager.EXTRA_DEVICE, usbDevice);
// Broadcast this event so we can receive it
sendBroadcast(broadcastIntent);
}
}
// Close the activity
finish();
}
}

Autostart (BOOT_COMPLETED) does not work on the Nomi tablet

My broadcast receiver triggered by event BOOT_COMPLETED and works fine on many devices except Nomi C10103.
The message appears in the log on the device:
D/ActivityManager: send broadcast: android.intent.action.BOOT_COMPLETED, skip package: com.example.myPackageName
Sending message am broadcast -a android.intent.action.BOOT_COMPLETED com.example.myPackageName from adb shell also does not run the application.
Manifest code:
<receiver
android:name=".AutoRunReceiver">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter android:priority="1000">
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
AutorunReceiver.java:
public class AutoRunReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//приёмник автозагрузки
if (intent != null && intent.getAction() != null) {
final String msg="intent:"+intent+" action:"+intent.getAction();
Log.d("myLogs",msg);
Toast.makeText(context,msg,Toast.LENGTH_LONG).show();
}
}
}
I realized that the problem is not in the code, but the device itself. Autoplay not working on my tablet for any application. As a workaround I have used the event "android.intent.action.USER_PRESENT", which is triggered after the device loading and screen unlocking.
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
its working fine for me.if its lenovo mobile you should wait at least 2 minutes then it will open automatically.

Manifest.xml, intent-filter and USB accessories

I currently have the following in my Manifest.xml
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<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" />
</activity>
Right now this will attempt to launch my application when a USB accessory is attached.
What changes do I need to make so that the application does NOT try to launch when an accessory is attached (assumed it is already launched) yet still will recognize the accessory when plugged in to the USB port?
You can simply create a Service/Intent Service that will be started instead of the Activity from the Application and you can perform the tasks in the Background.
A Broadcast Event occurs when USB is attached. You can simply receive it using the following code template.
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// UsbManager.ACTION_USB_STATE -> "android.hardware.usb.action.USB_STATE" actually
if (action.equals(UsbManager.ACTION_USB_STATE)) {
Bundle extras = intent.getExtras();
// UsbManager.USB_CONNECTED -> "connected" actually
usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED);
Source : USB Connection notification
I hope this helps.
...

Android JB broadcast receiver not receiving bluetooth.android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED

I am using a rooted samsung galaxy nexus phone running jb and for some reason I am not receiving any broadcast intents from the bluetooth connection service. Below you will find my receiver manifest and the broadcast receiver code. Any tips or ideas would be appreciated.
Thanks
Here is the Manifest
<receiver android:name=".ABroadcastReciever" >
<intent-filter>
<action android:name="android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED" />
<action android:name="android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED" />
<action android:name="android.bluetooth.BluetoothDevice.ACTION_BOND_STATE_CHANGED" />
<action android:name="android.bluetooth.BluetoothDevice.ACTION_FOUND" />
<action android:name="android.bluetooth.BluetoothDevice.BOND_BONDING" />
<action android:name="android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
Here is the Reciever
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//This is looking for the Wifi Connectivity Changes
if(action.equals("android.net.conn.CONNECTIVITY_CHANGE")){
Log.d(TAG,"received: Wifi Connection Change");
}
//This is looking Bluetooth connection disconnect
else if(action.equals("android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED") ||
action.equals("android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED_REQUESTED") ){
Log.d(TAG,"Received: Bluetooth Disconnected");
}
//This is looking for Bluetooth connection established
else if(action.equals("android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED")){
Log.d(TAG,"Received: Bluetooth Connected");
}
}
Here is the new intent that is being broadcast.
<action android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
Thanks for lookin
here is the new Intent
I don't know if I am correct, but I think that this line is wrong:
<action android:name="android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED" />
It should be this one:
<action android:name="android.bluetooth.device.ACTION_ACL_CONNECTED" />
Same thing with the others. Change "BluetoothDevice" for "device".
Also same thing similar in broascast :
if(action.equals("android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED"))
Should be
if(action.equals("android.bluetooth.device.action.ACL_CONNECTED"))
Change BluetoothDevice.ACTION_ACL_CONNECTED for device.action.ACL_CONNECTED
Your intent filter actions are incorrect. You actually want to use the following:
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
You can find these values in official Android BluetoothDevice documentation (look under constant value).

Categories

Resources