I noticed some very strange behavior when testing foreground notifications on Android 9.
Situation: I have some foreground services where I don't know how long they'll need to run for, be it 1 second or a whole minute. Finally, when the service is done, it will call stopForeground(true). This used to work fine on all Android 8, 9 and 10 devices, where stopForeground(true), even if called immediately, always reliably removed the notification.
Problem: Testing on a Fairphone 3 (and I hope someone else encountered this on some other devices, because for me this is not happening on any emulator or other device), stopForeground is not working as expected. Instead of immediately removing the notification, the notification always shows for at least 5 seconds, even if I call stopForeground straight away. These 5 seconds happen to be the exact 5 second limit of the dreaded error Context.startForegroundService() did not then call Service.startForeground() - still a problem. Very peculiar! Reproducing this and checking whether your device is affected is very easy with the code below.
Inside AndroidManifest.xml:
<service
android:name="<your package name>.TestService"
android:exported="false" />
Class TestService.java:
package <your package name>;
import android.annotation.TargetApi;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
#TargetApi(Build.VERSION_CODES.O)
public class TestService extends Service {
#Override
public void onCreate() {
super.onCreate();
showNotification();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
showNotification();
stopForeground(true);
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void showNotification() {
String channelId = "TEST";
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager.getNotificationChannel(channelId) == null)
notificationManager.createNotificationChannel(new NotificationChannel(channelId, "TEST NOTIFICATIONS", NotificationManager.IMPORTANCE_DEFAULT));
startForeground(1, new NotificationCompat.Builder(this, channelId).setContentText("TEST NOTIFICATION").build());
}
}
Finally, simply start the service (for example on button click) via startForegroundService(new Intent(this, TestService.class));
Has anyone else experienced this issue or is able to reproduce it with the code above? How can I fix or even just debug it, considering I'm testing on Android 9 and the behaviour is different simply because of the OEM?
Latest security patch claims that it's normal behaviour:
https://issuetracker.google.com/issues/147792378
However I'm not sure why it's happening already on Android 9.
Related
I want to start service on reboot of the device.
First I used broadcast receiver.It worked on my android phone having version 7. But I want to implement it on Xixun controller which is android based having version 4.0.3
Broadcast receiver did not work. I have gone through reading that, for API levels below 26, one should use WakefulBroadcastReceiver. When I did that, it worked on my phone but not on Xixun controller.
The process to reboot Xixun controller is just to power on and power off using supply.I have also used power intent of broadcast receiver. But it didn't work because device is batteryless.
What would be reason behind my service is not working?
[manifest receiver]
Following is service code
package com.example.serviceexample;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
public class MyService extends Service {
#Override
public void onCreate()
{
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast myToast =Toast.makeText(this,"service started",Toast.LENGTH_LONG);
myToast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0);
myToast.setDuration(Toast.LENGTH_LONG);
myToast.show();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
When I used broadcast receiver, codes are same except basic syntax.
I'm posing this as Q&A style because I found this idea working. And it's a fix to the hard problem to crack for beginners with Android.
Google has deprecated registering Broadcast Receiver into manifest like this below from API Level 26+ ( Except Some )
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
But, If one wants to receive particular device state changes like Internet Connectivity Changes (Which isn't allowed) while the app is in background and if it's important for any feature of his application, what should he do?
When I was going through the documentation, My eyes got stuck here :
Context-registered receivers receive broadcasts as long as their registering context is valid. For an example, if you register within
an Activity context, you receive broadcasts as long as the activity is
not destroyed. If you register with the Application context, you
receive broadcasts as long as the app is running.
That practically means if I can hold a Context, the broadcast-receiver registered with it will run in the background.
For doing that, a Service will be the best practice.
This is below code for a STICKY_SERVICE which is started again after killed and thus the context remains valid.
AlwaysOnService.class
package app.exploitr.auto;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.support.annotation.Nullable;
public class AlwaysOnService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
registerReceiver(new ClickReceiver(), new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));
return Service.START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onLowMemory() { // rem this if you want it always----
stopSelf();
super.onLowMemory();
}
}
Now, the receiver which actually does things :
ClickReceiver.class
package app.exploitr.auto;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import java.util.Objects;
public class ClickReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
switch (Objects.requireNonNull(intent.getAction())) {
case AutoJob.NOTIFICATION_CANCEL_TAG:
System.out.println("Not related");
break;
case AutoJob.LOGIN_CANCEL_TAG:
System.out.println("Not related");
break;
case "android.net.conn.CONNECTIVITY_CHANGE":
System.out.println("Oops! It works...");
break;
}
}
}
Launch Code From Any Activity Class
private void setUpBackOffWork() {
if (DataMan.getInstance(getBaseContext()).getPeriodic()) {
AutoJob.schedulePeriodic();
//Not related
}
if (DataMan.getInstance(getBaseContext()).getPureAutoLogin()) {
startService(new Intent(this, AlwaysOnService.class));
}
}
So my target was to Login into my isp automatically when I turn up my android's WiFi, and the code works smooth. It doesn't fail ever (It's running for 7 hours and 37 minutes till now and working well | not across reboots).
To keep the receiver running across reboots, try manifest registerable BOOT_COMPLETED actions. It works just like the old one.
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
Update 1
Now, as Google took one step to limit background execution & as a result you've also to make the service a foreground service. So, the procedure goes below.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, new Intent(THIS_SERVICE_CLASS_NAME.this, ACTIVITY_TO_TARGET.class), 0);
/*Handle Android O Notifs as they need channel when targeting 28th SDK*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel(
"download_check_channel_id",
"Channel name",
NotificationManager.IMPORTANCE_LOW);
if (notificationManager != null) {
notificationManager.createNotificationChannel(notificationChannel);
}
builder = new Notification.Builder(this.getBaseContext(), notificationChannel.getId())
.setContentTitle("Hi! I'm service")
.setContentIntent(pendingIntent)
.setOngoing(true);
notification = builder.build();
startForeground("StackOverflow".length(), notification);
}
return START_STICKY;
}
This also applies to Xamarin Android. The Play Store demanded to upgrade my apps's SDK to 8.0 Oreo, and a bunch of stuff stopped working on it.
Microsoft's documentation on Broadcast Receivers is quite confusing:
Apps that target Android 8.0 (API level 26) or higher may not statically register for an implicit broadcast. Apps may still statically register for an explicit broadcast. There is a small list of implicit broadcasts that are exempt from this restriction.
Even Google's official docs are quite inscrutable.
On Xamarin Android it used to be enough to follow this pattern:
[BroadcastReceiver]
[IntentFilter(new string[] {MyReceiver.MyAction})]
public class MyReceiver : BroadcastReceiver
{
public const String MyAction = "com.mytest.services.MyReceiver.MyAction";
public override void OnReceive (Context context, Intent intent)
{
// ...
}
}
The IntentFilter annotation instructs the compiler to add the receiver and intent filters registrations to the Manifest file during the build process. But from target SDKs v8.0 (Oreo/API 26) and above Android ignores these configurations on Manifest (except some system implicit actions). So this means that the IntentFilter annotations only works for those exceptions, and to make your broadcast receivers receive broadcasts it is required to register them on execution time:
#if DEBUG
[Application(Debuggable=true)]
#else
[Application(Debuggable=false)]
#endif
public class MyApplication: Application
{
public override void OnCreate ()
{
base.OnCreate ();
Context.RegisterReceiver(new MyReceiver(), new IntentFilter(MyReceiver.MyAciton));
}
}
It is also possible to register a receiver only for the lifecycle of an Activity, as explained by #Toaster. You can keep sending broadcasts normally:
// ...
ApplicationContext.SendBroadcast(new Intent(MyReceiver.MyAction));
// ...
I'm developing an Air (Flex) Mobile application for Android, which uses Air Native Extension (ANE) to take advantage of some platform functions that otherwise could not be used (at least to my knowledge). One of the platform functions that I want to use are services, specifically services that run as Foreground processes (using the startForeground() method).
When I invoke the service from my ANE everything works like a charm, the service is started correctly and it does what it needs to do but the problem is that Android seems to be trying to start it independently from my code, which, of course, results in errors that appear in the LogCat.
When I launch the application in debug mode in Flash Builder, and I use it for a while checking that the service works perfectly and no errors are thrown, after I close it from the Flash Builder (not from Eclipse ADT, which I also could) a couple of seconds later, the following errors appear:
01-16 10:56:06.953: E/AndroidRuntime(9757): java.lang.RuntimeException: Unable to start service com.mycompany.myextension.services.MyService#41594a50 with Intent { cmp=air.QOE.debug/com.mycompany.myextension.services.MyService }: java.lang.NullPointerException
01-16 10:56:06.953: E/AndroidRuntime(9757): at com.mycompany.myextension.services.MyService.onStartCommand(MyService.java:37)
It seems to be clear that Android tries to start the service but since its design to work inside the ANE -the extension is initialized but its context was already disposed- it crashes because it cannot reach the variables that are initialized within the context, therefore, ending in a crash or error the first time the code uses a context variable (line 37).
I would think that this has to do with the way I declared the service in my Android Manifest file. Next is part of the XML:
<application android:debuggable="true">
<service android:enabled="true" android:exported="true" android:name="com.mycompany.myextension.services.MyService">
<intent-filter>
<action android:name="air.com.mycompany.myextension.DO_CUSTOM_ACTION"/>
</intent-filter>
</service>
</application>
I hope you can tell me if I’m declaring the service incorrectly or if I’m making a mistake elsewhere. I appreciate the help.
EDIT: Service code
package com.mydomain.myapplicationextension.services;
import java.util.Timer;
import com.adobe.fre.FREContext;
import com.mydomain.myapplicationextension.myapplicationextension;
import com.mydomain.myapplicationextension.components.HttpServiceTask;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
public class MyApplicationService extends Service {
private Timer timer;
#Override
public IBinder onBind(Intent arg0) {
// Log.d("MyApplicationService", "onBind()");
return null;
}
#Override
public void onCreate() {
// Log.d("MyApplicationService", "onCreate()");
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Log.d("MyApplicationService", "onStartCommand(): " + myapplicationextension.applicationID);
Context appContext = myapplicationextension.applicationContext;
Intent launchIntent = appContext.getPackageManager().getLaunchIntentForPackage(myapplicationextension.appPackageName);
PendingIntent pendingLaunchIntent = PendingIntent.getActivity(appContext, 0, launchIntent, 0);
FREContext extContext = myapplicationextension.extensionContext;
int icon = extContext.getResourceId("drawable.notification_icon");
Notification notification = new Notification.Builder(appContext)
.setContentTitle(myapplicationextension.applicationID)
.setContentText(myapplicationextension.notificationMessage)
.setSmallIcon(icon)
.setContentIntent(pendingLaunchIntent)
.build();
startForeground(1,notification);
// Log.d("MyApplicationService", "startForegroundService()");
if(myapplicationextension.checkStatus)
{
timer = new Timer("Printer");
HttpServiceTask serviceTask = new HttpServiceTask(timer, launchIntent,myapplicationextension.statusServiceURL, myapplicationextension.triggerResponse);
timer.schedule(serviceTask, 0, 2000);
// Log.d("MyApplicationService", "startTimer()");
}
return START_STICKY;
}
#Override
public void onDestroy() {
// Log.d("MyApplicationService", "onDestroy():");
if(myapplicationextension.checkStatus)
{
timer.cancel();
timer = null;
// Log.d("MyApplicationService", "onDestroy(): timer.cancel():");
}
super.onDestroy();
}
}
When the phone runs out of memory and kills the service before it finishes executing. START_STICKY tells the OS to recreate the service after it has enough memory and call onStartCommand() again with a null intent. START_NOT_STICKY tells the OS to not bother recreating the service again. There is also a third code START_REDELIVER_INTENT that tells the OS to recreate the service AND redelivery the same intent to onStartCommand().
START_STICKY and START_NOT_STICKY
I have an ANE using START_STICKY that keeps launching the service
Hi fellow Android coders !
I'm currently writing an application that uses the new Android 4.3 notification listener to change the notification led color when a notification is received, and am facing a problem, probably due to my lack of knowledge about how notifications work.
So far, it is working quite well. When the user switch off the screen, I create a notification, with a custom led color, and I remove it when the screen is switched ON. My problem is that when the user switch the screen back ON, he can see my notification icon on the status bar for half a second just before it is removed. Not that big of a deal, but as a nit-picker, I can't help but try a way to avoid this ugly behavior. I know that some apps succeed as doing this - LightFlow for example.
My first idea was to play with the priority of the notification and using Notification.PRIORITY_MIN nearly worked : the notification icon is not displayed on the status bar but appears when the bar is expanded.
I unfortunately discovered that notifications with minimum priority doesn't toggle the notification led when the screen is off !
I then tried to create a notification with no icon, but it is not supported by the framework - that's actually a good thing.
And now I'm out of idea.
Can someone help my find a way to create a notification that doesn't appear on the status bar but still toggles the LED on ?
Or maybe should I remove my notification before the screen is actually switched ON but I can't find a way to do this either...
If someone can help me on this one, it would make my day !
Here is the code source of my application :
Main activity :
package com.nightlycommit.coloration;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
Service listener
package com.nightlycommit.coloration;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.service.notification.StatusBarNotification;
/**
* Created by Eric on 08/08/13.
*/
public class NotificationListenerService extends android.service.notification.NotificationListenerService {
private final String TAG = getClass().getSimpleName();
#Override
public void onCreate() {
super.onCreate();
// REGISTER RECEIVER THAT HANDLES SCREEN ON AND SCREEN OFF LOGIC
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new ScreenStateReceiver();
registerReceiver(mReceiver, filter);
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
String text = "Notification posted !";
}
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {
String text = "Notification removed !";
}
}
Screen state receiver :
package com.nightlycommit.coloration;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
/**
* Created by Eric on 12/08/13.
*/
public class ScreenStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
String action = intent.getAction();
if (action != null) {
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Notification.Builder builder = new Notification.Builder(context);
builder.setLights(Color.argb(255,255,0,0), 500, 500);
builder.setContentText("TEST");
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setOngoing(true);
// builder.setPriority(Notification.PRIORITY_MIN);
notificationManager.notify(777, builder.build());
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
notificationManager.cancel(777);
}
}
}
}
Thanks by advance,
Eric.
I know, there are tons of these on here, but I've been trying solutions all day and haven't gotten anywhere.
Neither the example on google's docs, nor any of the 5 other ways I've found on here have worked for me at all.
As is the typical case, when I click the notification it closes the status bar and nothing new is shown onscreen.
I am creating the notification from a service and need the notification to trigger a new activity that has not yet been created.
I also will need a way to pass information to that activity via intent.
And yes... this is java for Android
What follows are the shattered remnants of my code.
package com.bobbb.hwk2;
import java.io.FileOutputStream;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
import android.provider.ContactsContract;
import android.widget.Toast;
public class contactBackup extends Service
{
private NotificationManager nManager;
private static final int NOTIFY_ID = 1100;
#Override
public void onCreate()
{
super.onCreate();
String ns = Context.NOTIFICATION_SERVICE;
nManager = (NotificationManager)getSystemService(ns);
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
// inform user that service has started
Toast.makeText(getApplicationContext(), R.string.service_started,Toast.LENGTH_LONG).show();
String data = lookUpContacts();
if( saveToSDCard(getResources().getString(R.string.backup_file_name),data) )
{
Context context = getApplicationContext();
// create the statusbar notification
Intent nIntent = new Intent(this,contactViewer.class);//Intent nIntent = new Intent(Intent.ACTION_MAIN);
nIntent.setClass(context,contactViewer.class);
//nIntent.putExtra("data",data);
Notification msg = new Notification(R.drawable.icon,"All contacts records have been written to the file.",System.currentTimeMillis());
// start notification
//PendingIntent pIntent = PendingIntent.getService(getApplicationContext(),0,nIntent,PendingIntent.FLAG_UPDATE_CURRENT|Intent.FLAG_FROM_BACKGROUND);
PendingIntent pIntent = PendingIntent.getActivity(this,0,nIntent,0);
msg.flags = Notification.FLAG_AUTO_CANCEL;
msg.setLatestEventInfo(context,
"success",
"All contacts records have been written to the file.",
pIntent);
nManager.notify(NOTIFY_ID,msg);
}
}
#Override
public void onDestroy()
{
nManager.cancel(NOTIFY_ID);
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
// function returns string containing information
// from contacts
public String lookUpContacts()
{
...
}
public boolean saveToSDCard(String fileName, String data)
{
...
}
}
I can only hope that whatever is causing my problem is something fixable and not more of the crazy glitches I've been getting with eclipse (which no one else seems to have ever seen >:U )
If you can help me solve this problem, please share.
If you can't help with this specific problem but feel obligated to say unrelated things about posting, styles, topics, or good practice, then DON'T
Thank you :D
Edit:
You're going to have to add a flag for FLAG_ACTIVITY_NEW_TASK:
nIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
This is because you're launching from outside your app (from the system notification bar).
This is what happens when people overwork themselves. XD
The only reason none of the tutorials I tired worked is because I misspelled my activity name in the manifest.
Thanks for stopping by
Just add following in contactBackup(service class),
Intent nIntent = new Intent(this,contactViewer.class);//Intent nIntent = new Intent(Intent.ACTION_MAIN);
nIntent.setClass(context,contactViewer.class);
nIntent.putExtra("data",data);
nIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Notification msg = new Notification(R.drawable.icon,"All contacts records have been written to the file.",System.currentTimeMillis());
// start notification
//PendingIntent pIntent = PendingIntent.getService(getApplicationContext(),0,nIntent,PendingIntent.FLAG_UPDATE_CURRENT|Intent.FLAG_FROM_BACKGROUND);
PendingIntent pIntent = PendingIntent.getActivity(this,0,nIntent,0);
msg.flags = Notification.FLAG_AUTO_CANCEL;
msg.setLatestEventInfo(context,
"success",
"All contacts records have been written to the file.",
pIntent);
nManager.notify(NOTIFY_ID,msg);
then get value in contactViewer class,
as,
String s=getIntent().getStringExtra("data");