I'm developing a system app, and when I attempt to start it up from the home screen in Android 31, it fails and an java.lang.IllegalArgumentException is thrown. Here's what the stack trace looks like:
java.lang.IllegalArgumentException: tech.[brand].[name]: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
at android.app.PendingIntent.checkFlags(PendingIntent.java:375)
at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:645)
at android.app.PendingIntent.getBroadcast(PendingIntent.java:632)
at androidx.work.impl.utils.ForceStopRunnable.getPendingIntent(ForceStopRunnable.java:285)
at androidx.work.impl.utils.ForceStopRunnable.isForceStopped(ForceStopRunnable.java:158)
at androidx.work.impl.utils.ForceStopRunnable.forceStopRunnable(ForceStopRunnable.java:185)
at androidx.work.impl.utils.ForceStopRunnable.run(ForceStopRunnable.java:103)
at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
The stack doesn't reach any of my source code and far as I know I'm not calling any intent in my code so far, pending or otherwise. Does anyone know what's going on?
The androidx.work in the stack trace is WorkManager. As per the WorkManager 2.7.0-alpha02:
Make PendingIntent mutability explicit, to fix a crash when targeting Android 12.
Therefore you should upgrade to the latest version, WorkManager 2.7.1 by adding an explicit dependency on that latest version to the dependencies block of your build.gradle file:
implementation "androidx.work:work-runtime:2.7.1"
PendingIntent is a flag in API 21+ .So either you can set it like this:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
}
else {
PendingIntent.FLAG_UPDATE_CURRENT
}
OR
According to the docs here , one should choose FLAG_IMMUTABLE. Use FLAG_MUTABLE in case if some functionality depends on the PendingIntent
val updatedPendingIntent = PendingIntent.getActivity(
applicationContext,
NOTIFICATION_REQUEST_CODE,
updatedIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT // setting the mutability flag
)
Related
Okay, I know my title can be confusing so I'll explain a bit. I have access to an API and I would like that: every hour, my application, in the background, makes a request to this API and sends a notification if the API response contains a more or less recent date. To check if the answer is recent, I am already able to do it but what I would like to know is how to make this request in the background every hour and then how to send the data of this request in a notification (I already know how to create a notification, that's not the problem). I'm stuck for some time, I imagine that the answer will be something related to a server, a domain that I know absolutely nothing about
You can try AlarmManager and BroadcastReceiver with the repeating time 1 hour
Example:
val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else PendingIntent.FLAG_UPDATE_CURRENT
val intent = Intent(context, YourReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, flag)
val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager
alarmManager.setRepeating(
AlarmManager.RTC,
timeInMillis,
1000 * 60 * 60, //1h in millis
pendingIntent
)
Then write your YourReceiver and override onReceive function
class YourReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
//call api & check your requirements then push notification
}
}
Manifest:
<application>
...
<receiver android:name=".YourReceiver" />
</application>
Or you can try periodic work request:
https://medium.com/#sumon.v0.0/android-jetpack-workmanager-onetime-and-periodic-work-request-94ace224ff7d
If the API already exists, you don't need a server. You just need to schedule a job to be done on the device every hour. You can use WorkScheduler for that. If the API needs to be written, then yes you need a server and need to learn how to write a web service. But that's well beyond the size of a stack overflow question, you can google for a variety of tutorials on that.
When i'm trying to update my app - i got error during review process. Remediation for Implicit PendingIntent Vulnerability - https://support.google.com/faqs/answer/10437428. In my app there is on place, where i'm creating PendingIntent - for Firebase push notifications:
Inside class FCMService extends FirebaseMessagingService
#Override
public void onMessageReceived(#NotNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Intent intent = new Intent(this, ApplicationActivity.class);
intent.setAction("com.google.firebase.MESSAGING_EVENT");
intent.setPackage(getApplicationContext().getPackageName());
Map<String, String> data = remoteMessage.getData();
for (Map.Entry<String, String> entry : data.entrySet()) {
String value = entry.getValue();
String key = entry.getKey();
if (key.equals(ApplicationActivity.LINK_URL) ||
key.equals(ApplicationActivity.FLOCKTORY_LINK_URL)) {
intent.putExtra(ApplicationActivity.FLOCKTORY_LINK_URL, value);
if (remoteMessage.getNotification() != null && remoteMessage.getNotification().getTitle() != null) {
intent.putExtra(ApplicationActivity.HMS_PUSH_TITLE, remoteMessage.getNotification().getTitle());
}
}
}
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
RemoteMessage.Notification notification = remoteMessage.getNotification();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.channel_id))
.setSmallIcon(R.drawable.ic_launcher_notification)
.setColor(getResources().getColor(R.color.colorNotification))
.setContentTitle(notification == null ? "" : notification.getTitle())
.setContentText(notification == null ? "" : notification.getBody())
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(new Random(UUID.randomUUID().getLeastSignificantBits()).nextInt(), builder.build());
In Manifest:
<service
android:name="ru.svyaznoy.shop.domain.FCMService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
implementation "com.google.firebase:firebase-messaging:22.0.0"
minSdkVersion 24
targetSdkVersion 30
I just cant figure out what's wrong with this code - i pass explicit Intent with all required fields set. My head is blowing - this update is very important. Does anyone had similar issue?
Thanks to #kkazakov problem solved. Library com.huawei.hms:push contains unsafe usage of implicit PendingIntents. Google approved update for build without this lib.
For me it's time to create gms and hms build flavors to avoid problems with Huawei in the future.
The Intent in your example is an explicit intent with a given action. So this shouldn't be the cause for your update problem.
I am facing the same security problem and I think the cause for this is in a dependency. Because there are only explicit pending intents in my app.
I don't think that google prevents an update because of a vulnerability in their own libraries so I currently looking into the dependencies of the Huawei SDKs. It's just a guess but without any more information from play store guessing is the only thing we can do.
Thank you for your feedback. This issue has been resolved in the release of Push SDK 5.3.0.304.
It has been tested and verified by developers and can be approved by Google for release.
For details, you can check the Push kit Version Change History description.
Just want to update that our app used Push SDK 5.3.0.304, but still got the warning in the play console's Pre-launch report details.
It indicates that PushNotification.java's method with this signature: void a(android.content.Context,android.content.Intent,long,int) produces the issue.
Maybe #shirley could help to check if this method still has the issue. Thanks.
I'm new to android development and Java, and I'm slightly confused about the handling of backwards compatibility when using classes that have been introduced in the latest versions.
I've checked out the android support library and see the various XXXCompat classes available. For the ones I've looked at they appear to pretty much branch on VERSION.SDK_INT and call a new api or an old api.
I am using a support library (com.android.support:support-v4:27.1.1) version that is targeted for a newer api than my targetSdkVersion (25). I was under the impression this was the intended use case? to be able to write code with newer api's but have it work when targeting older sdk.
If so, how is this possible? For instance ContextCompat is has the following for startForegroundService
public static void startForegroundService(#NonNull Context context,
#NonNull Intent intent) {
if (VERSION.SDK_INT >= 26) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
}
However in the version I am targeting the Context doesn't have the method startForegroundService. If paste this if block into my code, it fails to compile with java: cannot find symbol ....
I can only assume that even if you compiled against a newer api (such that it could resolve the symbols), if those symbols don't exist at runtime, as long a they are not called it is not a problem?
So this is fine for api calls that are abstracted by the XXXCompat classes, but when using new classes like NotificationChannel. I can only import this if upping my targetSdkVersion to > 26. So assuming I do this, then this class is available. All uses of it that I have seen do
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel channel = ...
}
Does this mean that at runtime, for lower Build.VERSION.SDK_INT the symbol NotificationChannel will not exist? and if I attempted to instantiate this class it on a lower android version, it would crash?
Before Oreo, you can just start your service. For Oreo and higher, the service needs to run in foreground and thus post a notification upon service start otherwise it gets destroyed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
To post notification in Oreo and above you need to create a notification channel, before Oreo you just add your channel id to the notification builder (no need to create a channel). Snippet code from service:
String channelId = "myAppChannel"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// build notification channel
createNotificationChannel(channelId, ...)
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId)
// Build your notification
Notification notification = notificationBuilder
.setOngoing(true)
.setSmallIcon(icon)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
// post notification belonging to this service
startForeground(NOTIFICATION_ID, notification)
When you create createNotificationChannel function, just annotate it with #RequiresApi(26).
I have created a class which extends Service and runs as a foreground service. I would like my service notification to be persistent (i.e. not removed by swiping). However, my notification can be dismissed by swiping.
The Service documentation states: ...A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading. This means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground...
I did put break points to check whether onDestroy() or stopSelf() is hit, but this is not the case. The service is running in foreground mode, but I can dismiss the notification by swiping.
I have found quite a few questions regarding the opposite case, where it was not possible to dismiss the notification after the service was stopped, but did not find any question similar to my problem.
The service is started via an Intent and initialized as follows:
#Override
public void onCreate()
{
super.onCreate();
initialize();
}
private void initialize()
{
Notification n = get_service_notification();
startForeground(10, n);
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter(BroadcastCodes.service_broadcast_intent_name));
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
startServing();
return START_STICKY;
}
where startServing() subscribes to location updates
The notification is built like this
private Notification get_service_notification()
{
Intent intent = new Intent(this, LoginActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
b.setAutoCancel(false)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.app_icon)
.setTicker("some text")
.setContentTitle("some text")
.setContentText("some text")
.setContentIntent(contentIntent)
.setContentInfo("")
.setOngoing(true);
Notification res = b.build();
res.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
return res;
}
I have tried playing around with the flags and setOngoing/setAutoCancel, but the result stayed the same.
SDK versions are configured as minSdkVersion 18, targetSdkVersion 25
I would really appreciate any input regarding this issue - it might be just some stupid mistake, but I already did spend more than a few hours trying to research on my own...unfortunately to no avail.
EDIT 1
I still have not figured out the problem. I decided to try a dirty hack, but still no luck.
I made a PendingIntent and registered it via setDeleteIntent() the idea being to display a new notification once this one was dismissed by swipe. Unfortunately, I was not able to get this to work (the intent never fires when swiping).
I would also like to clarify, that the notification CAN BE swiped away, but IS NOT AFFECTED (i.e. is not deleted) by the clear-all button in the notification pane (the trash icon).
EDIT 2
As mentioned above I was not able to use setDeleteIntent() to recreate my notification when it is dismissed by swipe.
For now I settled with a workaround - my service does some periodical tasks. I am now calling
notificationManager.notify(10, service_notification);
when my task runs so that even if swiped away, my notification will be recreated after a while.
I still have a strong feeling that I just misread the documentation, since there are several notifications on my test device (Honor API23) that can not be swiped away.
EDIT 3
I have tried the same code on a different device (Lenovo API23) and the notification works as expected (can not be dismissed and can not be swiped). My problem seems to be device specific.
I made a few further tests (besides the ones mentioned in updates). I tried running various examples/tutorials from around the internet like this page here
On the Honor I was initially running my tests the notification could always be swiped, on the Lenovo and others the "ongoing" flag works as stated in the documentation, i.e. it can not be dismissed via swipe action.
To sum it up, my problem really seems to be device specific.
I've read a bunch of answers pertaining to this question and they all seem to be the same:
"Run your service with START_STICKY"
"Run your service in the foreground"
"Run your service with startService and don't bind it"
I'm doing ALL of these things, and my service STILL closes and restarts every time my activity is closed.
This is NOT an IntentService.
I'm also not calling stopSelf or stopService anywhere except in onClick handlers.
Please scroll down to my update - This behavior has been confirmed to be a bug in the android OS and I have reported it to google. Click here to view the report.
Starting my service from MainActivity:
svcIntent = new Intent(getBaseContext(), MyService.class);
startService(svcIntent);
In my onStartCommand:
// Enter foreground state
String title = "Service has been started...";
String subject = "Service is running...";
String body = "Monitoring your battery usage.";
Notification notification = new Notification(R.drawable.theicon, title,
System.currentTimeMillis());
if (prefs.getBoolean("notificationSounds", true))
notification.defaults |= Notification.DEFAULT_SOUND;
else
notification.sound = null;
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, subject, body, pendIntent);
startForeground(1500, notification);
At the end of my onStartCommand:
...
// Release WakeLock
wl.release();
return START_STICKY;
UPDATE
I FIGURED OUT WHATS CAUSING IT! But I have no idea how to fix it. In my service i also use an AlarmManager inside my service to set up function calls to the service a specified time away.
// Alarm manager setup for MyService
AlarmManager AM = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
svcIntent1 = new Intent(this, AlarmReceiver.class);
prefs.edit().putInt("initialBatt", initialBatt).apply();
svcIntent1.setAction("com.myApp.servicealarm");
pendingIntent = PendingIntent.getBroadcast(this, 93, svcIntent1, PendingIntent.FLAG_UPDATE_CURRENT);
// Set the alarm
AM.set(AlarmManager.RTC_WAKEUP, timeNow + waitTime, pendingIntent);
I noticed that if I DO NOT comment out the AM.set call to set an alarm, EVEN WITH AN EMPTY onReceive, my service is killed when the alarm goes off, after I swipe my app away in recent apps. If I comment out the set alarm call, then the service is never killed and keeps running after I close my app. What the heck?! I need this alarm for the functionality of my algorithms!!
It's very odd. As soon as the alarm goes off, my debug message does not print, and my service restarts. But the second time around, after the service restarts, the debug message does print and the program executes successfully.
I've tried this and it still happens with a normal broadcast receiver as well. I've also stripped my code down to ONLY the set alarm call from my service and the broadcast receiver, and the same thing occurs so it's not my algorithm. Apparantly if you have a foreground service that sets an alarm, when the alarm goes off your service restarts.
CLOSING
This behavior seems to be caused by a bug in the android OS so I do not expect an answer. If you'd like to see this bug for yourself, click here. I've provided a project that you can compile and reproduce the problem with.
Android kills the process when the broadcast Intent is sent (before it is received/processed in your app).
This is a nasty Android bug, as of 4.4 (API 19).
See https://code.google.com/p/android/issues/detail?id=63618&can=1&q=service%20restart%20broadcast&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
especially comments #22 and #23
Unfortunately, almost all "open" issues were marked as "obsolete" recently, with the assumption that they were all fixed in Android 5.0. There's no way for a developer to reopen an "obsolete" issue.
EDIT: Add details about foreground broadcast
Based on the information in the linked issue, it looks like adding Intent.FLAG_RECEIVER_FOREGROUND to your broadcast Intent will ensure that the the process does not get killed on the next receipt of a broadcast Intent.
To do this, add:
svcIntent1.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
to the code where you set your alarm in the AlarmManager.
Please read the comments in the linked issue for more details.
Try to run your service in separate process. Define it in your manifest like this:
<service
android:name=".path.to.service.class"
android:process=":my_service"/>