Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 4 years ago.
Improve this question
I know how to detect headphone plug in event if my application is running. You have to register broadcast receiver for ACTION_HEADSET_PLUG. But you can't capture this action using Manifest declaration for broadcast receiver. Therefore the only option to capture headphone plug in event is background service. But it drains battery and so on.
I checked out some music players and figured out that they capture that event without any additional services. How do they do that? Any ideas?
I've had an app in the store for three years that monitors both the wired headset and bluetooth state and nobody has ever complained about battery drain. But that is because I am successfully using a single service and broadcast receiver for detecting events from both. Here's the two key classes:
public class HeadsetStateBroadcastReceiver extends BroadcastReceiver {
public static final String[] HEADPHONE_ACTIONS = {
Intent.ACTION_HEADSET_PLUG,
"android.bluetooth.headset.action.STATE_CHANGED",
"android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"
};
#Override
public void onReceive(final Context context, final Intent intent) {
boolean broadcast = false;
// Wired headset monitoring
if (intent.getAction().equals(HEADPHONE_ACTIONS[0]) {
final int state = intent.getIntExtra("state", 0);
AudioPreferences.setWiredHeadphoneState(context, state > 0);
broadcast = true;
}
// Bluetooth monitoring
// Works up to and including Honeycomb
if (intent.getAction().equals(HEADPHONE_ACTIONS[1])) {
int state = intent.getIntExtra("android.bluetooth.headset.extra.STATE", 0);
AudioPreferences.setBluetoothHeadsetState(context, state == 2);
broadcast = true;
}
// Works for Ice Cream Sandwich
if (intent.getAction().equals(HEADPHONE_ACTIONS[2])) {
int state = intent.getIntExtra("android.bluetooth.profile.extra.STATE", 0);
AudioPreferences.setBluetoothHeadsetState(context, state == 2);
broadcast = true;
}
// Used to inform interested activities that the headset state has changed
if (broadcast) {
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("headsetStateChange"));
}
}
}
Here is the service I use to register the broadcast receiver:
public class HeadsetMonitoringService extends Service {
HeadsetStateBroadcastReceiver headsetStateReceiver;
#Override
public void onCreate() {
headsetStateReceiver = new HeadsetStateBroadcastReceiver();
final IntentFilter filter = new IntentFilter();
for (String action: HeadsetStateBroadcastReceiver.HEADPHONE_ACTIONS) {
filter.addAction(action);
}
registerReceiver(headsetStateReceiver, filter);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
return START_STICKY;
}
#Override
public void onDestroy() {
unregisterReceiver(headsetStateReceiver);
}
#Override
public IBinder onBind(final Intent intent) {
return null;
}
}
And here is my manifest entry:
<service
android:name=".services.HeadsetMonitoringService"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="initialiseHeadsetService" />
</intent-filter>
</service>
How it works is as follows:
I use an on boot broadcast receiver to send a start service message to the HeadsetMonitoringService (you don't have to do it this way, you could just do this when your application starts instead). The HeadsetMonitoringService in turn registers an instance of a broadcast listener that listens to all the headset events I am interested in - they are held in the HEADPHONE_ACTIONS array. Because the service is sticky it hangs around - and therefore so does the broadcast listener. But because both the service and the broadcast listener are event driven they do not consume any power until a headset state change occurs. Additionally, because the service is sticky, it will be restarted by the OS if it dies unexpectedly.
Whenever I receive a headset state change event I also fire a local broadcast so that interested activities can check the new state and take action if required.
For completeness, I should point out that I use another class (not shown here), AudioPreferences, to store as preferences both the Bluetooth and wired headset state, which can then be accessed whenever I need to know the headset state.
Your application will need the android.permission.BLUETOOTH permission if you are interested in the state of a Bluetooth headset. If not, just take out the Bluetooth related actions from the HEADPHONE_ACTIONS array and delete the associated if blocks from the onReceive method.
Related
I have the following service declared in the manifest:
<application
...
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.example.android.test.TestService"
android:process=":Remote"
android:permission="android.permission.WAKE_LOCK"/>
<receiver android:name="com.example.android.test.TestService">
<intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
</intent-filter>
</receiver>
</application>
and this is the Service class
public class TestService extends Service implements SensorEventListener {
public class BatteryReceiver_andFileChecker extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//TODO
}
// constructor
public BatteryReceiver_andFileChecker(){
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
//TODO
return START_STICKY;
}
#SuppressLint("WakelockTimeout")
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "Logging service started new", Toast.LENGTH_SHORT).show();
//Acquire wake lock
PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WLTAG:MyWakelockTag");
wakeLock.acquire();
//Display notification
this.notIntent = new Intent(this, MainActivity.class);
this.pendingIntent = PendingIntent.getActivity(this, 0, this.notIntent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, this.channelID).setSmallIcon(R.drawable.ic_launcher_background).setContentTitle("Test").setContentText("Sensor is recording").setPriority(NotificationCompat.PRIORITY_DEFAULT).setContentIntent(this.pendingIntent);
startForeground(this.NOTIFICATION, builder.build());
//BatteryCheck
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mReceiver = new BatteryReceiver_andFileChecker();
registerReceiver(mReceiver, filter);
}
#Override
public void onDestroy() {
super.onDestroy();
//cancel notification
stopForeground(true);
//Unregister battery receiver
unregisterReceiver(mReceiver);
//release wakeLock
wakeLock.release();
//Stop Service
stopSelf();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
// super.onBind(intent);
return null;
}
#Override
public void onSensorChanged(SensorEvent event) {
//TODO
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
And this services is created and terminated on two different "onclick" functions from the MainActivity
public class MainActivity extends AppCompatActivity {
...
public void onClickStart(View v) {
// Start Service
this.intent = new Intent(getApplicationContext(), TestService.class);
this.intent.putExtra("foo",foo);
startService(this.intent);
}
public void onClickStopAcquisition(View v) {
// Stop Service
stopService(this.intent);
}
}
This code runs as expecten in Android 6.0.1 and does not work on Android 10...
When I debug de app, no errors are fired when pressed the Start and Stop buttons in both OS, nevertheless, android 6.0.1 fires the service and android 10 does not...
Any reason why?
Just to keep in mind when understanding my implementation, my intention is to make a service which keeps running and doing stuff even if the user is not active in the application. Since I implementen the SensorEventListener to record data from sensors, my intention is to record data while the user might be interacting with the phone or even doing nothing (after he presses the power button of the phone, the service keeps running acquiring data and performing actions)
Then, the service should be terminated either when the user clicks the stop button or either when the MainActivity is terminated.
Thank you!
i guess you should read more about background services and broadcast in android API 26 or higher
From the official documentation available here
If an app registers to receive broadcasts, the app's receiver consumes resources every time the broadcast is sent. This can cause problems if too many apps register to receive broadcasts based on system events; a system event that triggers a broadcast can cause all of those apps to consume resources in rapid succession, impairing the user experience. To mitigate this problem, Android 7.0 (API level 24) placed limitations on broadcasts, as described in Background Optimization. Android 8.0 (API level 26) makes these limitations more stringent.
Apps that target Android 8.0 or higher can no longer register
broadcast receivers for implicit broadcasts in their manifest. An
implicit broadcast is a broadcast that does not target that app
specifically. For example, ACTION_PACKAGE_REPLACED is an implicit
broadcast, since it is sent to all registered listeners, letting them
know that some package on the device was replaced. However,
ACTION_MY_PACKAGE_REPLACED is not an implicit broadcast, since it is
sent only to the app whose package was replaced, no matter how many
other apps have registered listeners for that broadcast. Apps can
continue to register for explicit broadcasts in their manifests. Apps
can use Context.registerReceiver() at runtime to register a receiver
for any broadcast, whether implicit or explicit.
Broadcasts that require a signature permission are exempted from this restriction, since these broadcasts are only sent to apps that are signed with the same certificate, not to all the apps on the device
you should work with JobScheduler
Important Update
to answer your question in comment : use WorkManager for deferrable background tasks.
This library is backward compatible
It use JobScheduler,FirebaseJobDispatcher or AlarmManager
No Need to depend on play service library.
Recommended by Google for deferrable background work.
Can use features like chaining, constraints etc.
My Android application is continuously using the microphone and wants to release the microphone to other applications such as VOIP Calls.
I have registered broadcast receiver in the Manifest. But the microphone is not getting released.No sim card in the phones, I am only interested in detecting SIP sessions.
Here is my code in the Manifest
<receiver
android:name=".SipCallReceiver"
android:enabled="true"
android:exported="true">
</receiver>
Broadcastreceiver class
public class SipCallReceiver extends BroadcastReceiver {
ListeningActivity LA = new ListeningActivity();
#Override
public void onReceive(Context context, Intent intent) {
SipAudioCall call = null;
final SipAudioCall.Listener listener = new SipAudioCall.Listener() {
#Override
public void onCallEnded(SipAudioCall call) {
LA.setflgphone(false); LA.onPstop();
}
#Override
public void onCallEstablished (SipAudioCall call) {
LA.setflgphone(true); LA.Lstop();
}
};
}
}
Can anyone please let me know the mistake I am making here.
Thank you
I was able to solve it.
I created two services.
The first service monitors the Audio Manager at schedule interval with ScheduledExecutorService. This will get the mode of the Audio Manager.
When there is no communication, the mode will be 0.
This status broadcast to a locally registered broadcast receiver. The broadcast receiver will stop and start the second service which using the microphone depending on the audio mode.
I program in recent years to Android and I wonder something:
How to detect the presence of headphones?
There is a method: isWiredHeadsetOn() but it doesn't work.
I've tried that but it doesn't work:
AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
Log.i("am.isWiredHeadsetOn()", am.isWiredHeadsetOn() + "");
if (am.isWiredHeadsetOn()) {
//code
}
Thank you (and sorry if I made spelling mistakes, I am French)
#Phil answer at https://stackoverflow.com/a/19102661/4758255 is a good implementation to detect the headphone.
Here I'm quoting from his answer. Please upvoted his answer not this!
Upvote the answer: https://stackoverflow.com/a/19102661/4758255
I've had an app in the store for three years that monitors both the wired headset and bluetooth state and nobody has ever complained about battery drain. But that is because I am successfully using a single service and broadcast receiver for detecting events from both. Here's the two key classes:
public class HeadsetStateBroadcastReceiver extends BroadcastReceiver {
public static final String[] HEADPHONE_ACTIONS = {
Intent.ACTION_HEADSET_PLUG,
"android.bluetooth.headset.action.STATE_CHANGED",
"android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"
};
#Override
public void onReceive(final Context context, final Intent intent) {
boolean broadcast = false;
// Wired headset monitoring
if (intent.getAction().equals(HEADPHONE_ACTIONS[0]) {
final int state = intent.getIntExtra("state", 0);
AudioPreferences.setWiredHeadphoneState(context, state > 0);
broadcast = true;
}
// Bluetooth monitoring
// Works up to and including Honeycomb
if (intent.getAction().equals(HEADPHONE_ACTIONS[1])) {
int state = intent.getIntExtra("android.bluetooth.headset.extra.STATE", 0);
AudioPreferences.setBluetoothHeadsetState(context, state == 2);
broadcast = true;
}
// Works for Ice Cream Sandwich
if (intent.getAction().equals(HEADPHONE_ACTIONS[2])) {
int state = intent.getIntExtra("android.bluetooth.profile.extra.STATE", 0);
AudioPreferences.setBluetoothHeadsetState(context, state == 2);
broadcast = true;
}
// Used to inform interested activities that the headset state has changed
if (broadcast) {
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("headsetStateChange"));
}
}
}
Here is the service I use to register the broadcast receiver:
public class HeadsetMonitoringService extends Service {
HeadsetStateBroadcastReceiver headsetStateReceiver;
#Override
public void onCreate() {
headsetStateReceiver = new HeadsetStateBroadcastReceiver();
final IntentFilter filter = new IntentFilter();
for (String action: HeadsetStateBroadcastReceiver.HEADPHONE_ACTIONS) {
filter.addAction(action);
}
registerReceiver(headsetStateReceiver, filter);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
return START_STICKY;
}
#Override
public void onDestroy() {
unregisterReceiver(headsetStateReceiver);
}
#Override
public IBinder onBind(final Intent intent) {
return null;
}
}
And here is my manifest entry:
<service
android:name=".services.HeadsetMonitoringService"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="initialiseHeadsetService" />
</intent-filter>
</service>
How it works is as follows:
I use an on boot broadcast receiver to send a start service message to the HeadsetMonitoringService (you don't have to do it this way, you could just do this when your application starts instead). The HeadsetMonitoringService in turn registers an instance of a broadcast listener that listens to all the headset events I am interested in - they are held in the HEADPHONE_ACTIONS array. Because the service is sticky it hangs around - and therefore so does the broadcast listener. But because both the service and the broadcast listener are event driven they do not consume any power until a headset state change occurs. Additionally, because the service is sticky, it will be restarted by the OS if it dies unexpectedly.
Whenever I receive a headset state change event I also fire a local broadcast so that interested activities can check the new state and take action if required.
For completeness, I should point out that I use another class (not shown here), AudioPreferences, to store as preferences both the Bluetooth and wired headset state, which can then be accessed whenever I need to know the headset state.
Your application will need the android.permission.BLUETOOTH permission if you are interested in the state of a Bluetooth headset. If not, just take out the Bluetooth related actions from the HEADPHONE_ACTIONS array and delete the associated if blocks from the onReceive method.
If you are OK with Marshmallow and up the AudioDeviceCallback might be what you are looking for. It works with an AudioManager and tells you when anything connects and disconnects.
AudioManager.isWiredHeadsetOn() appeared to be the right thing to do. According to the Android developer doc :
Checks whether a wired headset is connected or not.
This is not a valid indication that audio playback is actually over the wired headset as audio routing depends on other conditions.
Returns
true if a wired headset is connected. false if otherwise
But :
you have to add the associated permission to your manifest (MODIFY_AUDIO_SETTINGS)
according to this post, it doesn't work well with Bluetooth headset.
This is for a GPS. I have a parent class with an embedded receiver class, and a separate LocationTrackingService class that handles the GPS stuff. I need to Broadcast the mileage traveled to update the UI, but the broadcast is never received. This is the only BroadcastReceiver in the project. I guess I could set a timer to have my ServiceConnection check every couple of seconds and grab the new mileage, but that's bad coding.
Nothing is in the Manifest because I'm registering and unregistering dynamically.
public class Parent
{
GPSReceiver gpsreceiver;
public class EmbeddedReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context arg0, Intent intent)
{
Bundle extras = intent.getExtras();
if (extras != null) {
distance = extras.getDouble(LocationTrackingService.UPDATE_MILEAGE_MESSAGE);
}
}
}
#Override
public void onCreate(Bundle savedInstanceState)
{
gpsReceiver = new EmbeddedReceiver();
}
private void gpsStart()
{
if (gpsReceiver != null) {
intentFilter = new IntentFilter();
intentFilter.addAction("don't know what goes here");
LocalBroadcastManager.getInstance(this).registerReceiver(gpsReceiver, intentFilter);
}
}
private void gpsStop()
{
if (gpsReceiver != null) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(gpsReceiver);
}
}
}
public class LocationTrackingService extends Service
{
private LocalBroadcastManager broadcaster;
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
broadcaster = LocalBroadcastManager.getInstance(this);
return START_STICKY;
}
.... code
private void sendResult(String message)
{
Intent i = new Intent("ParentActivity");
i.setAction("ParentActivity");
if (message != null) {
i.putExtra(message, mileageRunningTotal);
}
broadcaster.sendBroadcast(i);
}
}
When I follow the code into LocalBroadcastManager, on line 215 it does mActions.get(intent.getAction() to get an ArrayList<ReceiverRecord>, and it's null, but I don't know why.
I appreciate any help you can give.
Broadcasts work in such a way that the action acts as a trigger for the receiver. In other words, there are tons of broadcasts being sent around throughout your phone at any given time, the goal of the receiver is to catch the broadcast with the corresponding action when it flies by. It will let all other broadcasts continue through without interruption. Once it finds the one it is looking for, it will receive it and perform the onReceive() functionality.
Though an action can be any string key you care for it to be, it is advised to add in your package name. This gives specificity to your broadcast and allows your broadcast to be more easily managed in the barrage of broadcasts that your phone is sending. This is important as broadcasts can be sent between applications. It makes it so you avoid the following scenario
Application A sends out a system broadcast with action "SOME_ACTION" which we have no interest in. Application B will also be sending out a local broadcast with action "SOME_ACTION" which we are awnt to receive. We will setup Receiver 1 to look for and receive the action "SOME_ACTION" from Application B. However, because of conflicting actions, when Application A sends out a broadcast of "SOME_ACTION", we will inappropriately receive it in Receiver 1 and perform our onReceive() functionality as though we had just received a local broadcast from Application B.
Following recommended convention, you avoid the above situation by doing the following
Instead of setting your action as "SOME_ACTION", it would be set to "com.app_b.package.SOME_ACTION". That way when the broadcast action "com.app_a.package.SOME_ACTION" passes by, it won't be confused for our action and will be allowed to pass.
There may be other reasons for using package name, and this may not be the best of them, but to the best of my knowledge this is the reasoning behind the convention.
I cant see the declaration for the broadcaster object.
broadcaster.sendBroadcast(i);
Is it a Local Broadcast Manager instance?
If not it won't work.
My goal is print log when change wi-fi state.
I use below code.
MainActivity.java (Main Activity)
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this,WiFiService.class));
}
#Override
protected void onStart()
{
super.onStart();
Log.d("Start Service", "Start Service");;
startService(new Intent(this,WiFiService.class));
}
}
WiFiService.java (Service)
public class WiFiService extends Service
{
WiFiBroadCasetReceiver brod;
#Override
public IBinder onBind(Intent intent)
{
return null;
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
brod=new WiFiBroadCasetReceiver();
this.registerReceiver(brod, new IntentFilter(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
}
#Override
public void onDestroy()
{
super.onDestroy();
}
}
WiFiBroadCasetReceiver.java (BroadcastReceiver)
public class WiFiBroadCasetReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context arg0, Intent arg1)
{
Log.d("on receiver", "receiver");
}
}
AndroidManifest.xml
<application>
........
........
<receiver android:name=".WiFiBroadCasetReceiver" >
<intent-filter>
<action android:name="android.net.wifi.supplicant.STATE_CHANGE" />
</intent-filter>
</receiver>
</application>
Problem :
Above code is working fine in android 4.0 and lower versions. When I change state of wi-fi, broadcast receiver is call random times. So, log is print random times. I need only one time. It work fine all android version remains android 4.1.0. or higher version(Jelly Bean). I use android.net.wifi.WIFI_STATE_CHANGED. But still same error occur.
Several points should be addressed in your question.
First, you might be confusing the state of "android.net.wifi.supplicant.STATE_CHANGE" and "android.net.wifi.WIFI_STATE_CHANGED". And I think what you really want is the later one. See the comment from the source code.
/**
* Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
* enabling, disabling, or unknown. One extra provides this state as an int.
* Another extra provides the previous state, if available.
*
* #see #EXTRA_WIFI_STATE
* #see #EXTRA_PREVIOUS_WIFI_STATE
*/
#SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WIFI_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_STATE_CHANGED";
and
/**
* Broadcast intent action indicating that a connection to the supplicant has
* been established (and it is now possible
* to perform Wi-Fi operations) or the connection to the supplicant has been
* lost. One extra provides the connection state as a boolean, where {#code true}
* means CONNECTED.
* #see #EXTRA_SUPPLICANT_CONNECTED
*/
#SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION =
"android.net.wifi.supplicant.CONNECTION_CHANGE";
Second, why you got multiple broadcast callbacks? And the number of times is random? I think you might need to check your code carefully:
1. You start the service twice, once in Activity.onCreate() and once in Activity.onStart()
2. You register your broadcast receiver twice, once in AndroidManifest.xml and once in Service.onStart()
3. The most important thing is that you will create a new instance of your broadcast receiver instance in your Service.onStart(). That is to say, whenever your service is start, a new receiver will be created and registered. And looking back on 1, you see every time you bring you Activity back will call the service to start again.
So the random times of callback is because your bad code. Please just remove all the broadcast registers, left only the one in the AndroidManifest.xml
Finally, why you cannot make it work on JellyBean later? I think this is because you haven't specified the correct action. Please try "android.net.wifi.WIFI_STATE_CHANGED" instead of "android.net.wifi.supplicant.STATE_CHANGE" and try again.
The broadcast is received whenever the supplicant state of the wifi changes. Since over the course of establishing a connection this will change multiple times, multiple broadcasts are expected. (I remember having seen this on 2.3 devices also but I mostly use the connectivity change broadcast to check connectivity change so I might be incorrect).
The workaround you can do is, in your broadcast receiver check the extras include in the intent which indicates the SupplicantState. If the supplicant state in the extra is equal to SupplicantState.COMPLETED (wifi is connected and authenticated) then only implement your app logic otherwise ignore the broadcast.