Google denied update due Remediation for Implicit PendingIntent Vulnerability - java

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.

Related

How to receive a notification every hour if a condition is met on android studio?

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.

Android NotificationChannel compatability with older API

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).

Unique PendingIntent with Manifest declaration

I am creating an Android application which programmatically creates alarms which persist when the application is closed. In order to have the alarms persist, I had to define the receiver in my Manifest like so:
<receiver
android:name="com.blah.blah.AlarmReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter android:priority="999" >
<action android:name="com.blah.blah.ALARM" />
</intent-filter>
</receiver>
Here is how I am currently creating the PendingIntents..
Intent intent = new Intent(ACTION_ALARM);
PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager manager = (AlarmManager (this.getSystemService(Context.ALARM_SERVICE ));
Since I am using the constant ACTION_ALARM, all alarms are connected to the same PendingIntent, so calling .cancel() on that deletes all alarms. However, I want to delete specific alarm instances. The only way I know of to create a unique PendingIntent is to specify a unique action, which conflicts with defining my Receiver in the Manifest as I have above. Is there any other way to make distinguishable PendingIntents for my alarms? I tried adding Data as well, but it seemed the Receiver in the Manifest was not being triggered.
Alternatively, if I start a receiver for each alarm programmatically, is there a way to have those persist when the application is closed?
Thanks.
Found the solution here: How to create different pendingintent so filterEquals() return false?
Using the second parameter of PendingIntent.getBroadcast works to establish a unique PendingIntent. There is no need for a unique Action, though that is what every other post I could find suggested.
Not sure how to close the question now...

Service stops when activity is closed

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"/>

Notification DeleteIntent broken on later versions of Android

In our app OneBusAway Android (open-source on Github), we need to be notified when the user dismisses a particular reminder notification, so we don't post another reminder notification for the same event (how long until their bus arrives).
We're doing this by listening for an Intent in our app, registered as the DeleteIntent with the Notification. When the user dismisses the notification (either by swiping it away, or tapping the clear button in the notification window), our app should receive that Intent.
From testing, it seems that with the current version on Google Play (and the current master branch on Github), the DeleteIntent is never received in our application in the following versions of Android:
Android 4.4.3
Android 4.4.4
However, the exact same code DOES work (i.e., the Intent registered as the DeleteIntent is received by the app) on:
Android 2.3.3
Android 2.3.6
Android 4.1.1
Android 4.1.2
I've looked at the following SO posts that deal with DeleteIntent, and none of the solutions listed work on Android 4.4.3 and 4.4.4:
Notification Auto-Cancel does not call DeleteIntent
Android - DeleteIntent, how to use?
Notification deleteIntent does not work
https://stackoverflow.com/questions/24218626/how-to-detect-notification-cancel-event-in-android-not-deleteintent
https://stackoverflow.com/questions/22769523/why-my-deleteintent-is-not-working-on-my-notification
Android deleteIntent not working? What's wrong with my code?
Custom actions using implicit intents between applications
The current working master branch uses a Service to listen for the Intent. However, based on some of the above posts, I did tweak some of the code to be more in line with working examples that use a BroadcastReceiver to listen for the Intent.
The code using the BroadcastReceiver is in the following Github branch:
https://github.com/CUTR-at-USF/onebusaway-android/tree/issue104-RepeatingReminders
Below are excerpts for what my current version looks like (that still works on Android 4.1.2 and lower, but not 4.4.3 or 4.4.4), along with links to Github source:
Creating the notification
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/java/com/joulespersecond/seattlebusbot/tripservice/NotifierTask.java#L131
private Notification createNotification(Uri alertUri) {
//Log.d(TAG, "Creating notification for alert: " + alertUri);
Intent deleteIntent = new Intent(mContext, AlarmReceiver.class);
deleteIntent.setAction(TripService.ACTION_CANCEL);
deleteIntent.setData(alertUri);
return new NotificationCompat.Builder(mContext)
.setSmallIcon(R.drawable.ic_stat_notification)
.setDefaults(Notification.DEFAULT_ALL)
.setOnlyAlertOnce(true)
.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build();
}
Title and other dynamic notification info are set a few lines later (and reset later, if the notification remains undismissed):
#SuppressWarnings("deprecation")
private void setLatestInfo(Notification notification,
String stopId,
String routeId,
long timeDiff) {
final String title = mContext.getString(R.string.app_name);
final PendingIntent intent = PendingIntent.getActivity(mContext, 0,
new ArrivalsListActivity.Builder(mContext, stopId).getIntent(),
PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(mContext,
title,
getNotifyText(routeId, timeDiff),
intent);
}
TripService contains the constants for the action:
public static final String ACTION_CANCEL =
"com.joulespersecond.seattlebusbot.action.CANCEL";
AlarmReceiver
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/java/com/joulespersecond/seattlebusbot/AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
private static final String TAG = "AlarmReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "In onReceive with intent action " + intent.getAction());
...
}
}
AndroidManifest
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/AndroidManifest.xml
<receiver android:name=".AlarmReceiver">
<!-- These action names must match the constants in TripService -->
<intent-filter>
<action android:name="com.joulespersecond.seattlebusbot.action.SCHEDULE" />
<action android:name="com.joulespersecond.seattlebusbot.action.POLL" />
<action android:name="com.joulespersecond.seattlebusbot.action.CANCEL" />
</intent-filter>
</receiver>
With the above, on Android 4.4.3/4.4.4, the AlarmReceiver never sees the Intent when the user dismisses the notification.
I also tried adding a MIME type, as specified in Custom actions using implicit intents between applications, but that didn't work on Android 4.4.3/4.4.4 either:
Intent deleteIntent = new Intent(mContext, AlarmReceiver.class);
deleteIntent.setAction(TripService.ACTION_CANCEL);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
deleteIntent.setDataAndTypeAndNormalize(alertUri, TripService.REMINDER_MIME_TYPE);
} else {
deleteIntent.setDataAndType(alertUri, TripService.REMINDER_MIME_TYPE);
}
return new NotificationCompat.Builder(mContext)
.setSmallIcon(R.drawable.ic_stat_notification)
.setDefaults(Notification.DEFAULT_ALL)
.setOnlyAlertOnce(true)
.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
deleteIntent, 0))
//.setLights(0xFF00FF00, 1000, 1000)
//.setVibrate(VIBRATE_PATTERN)
.build();
REMINDER_MIME_TYPE is application/vnd.com.joulespersecond.seattlebusbot.reminder
Manifest for using the MIME type:
<receiver android:name=".AlarmReceiver">
<!-- These action names must match the constants in TripService -->
<intent-filter>
<action android:name="com.joulespersecond.seattlebusbot.action.SCHEDULE" />
<action android:name="com.joulespersecond.seattlebusbot.action.POLL" />
<action android:name="com.joulespersecond.seattlebusbot.action.CANCEL" />
<data android:mimeType="application/vnd.com.joulespersecond.seattlebusbot.reminder" />
</intent-filter>
</receiver>
I also tried not using the support library (i.e., using Notification.Builder instead of NotificationCompat.Builder), but that didn't change anything either.
Any ideas why this isn't working on Android 4.4.3/4.4.4?
More info is shown in the Github issue for this problem.
EDIT
I've also replicated this issue in a small Github project "DeleteIntentDemo":
https://github.com/barbeau/DeleteIntentDemo
Instructions to reproduce are in the README for this project.
EDIT 2
This appears to be due to a bug in Android in Notification.setLatestEventInfo() - I've reported it here:
https://code.google.com/p/android/issues/detail?id=73720
Please see #CommonsWare's answer for the workaround.
EDIT 3
My AOSP patch to fix this issue has now been merged so this problem won't appear for legacy apps in future releases of Android:
https://code.google.com/p/android/issues/detail?id=73720#c4
However, in the above AOSP thread is it emphasized that one should no longer be using Notification.setLatestEventInfo() - instead, use Notification.Builder to create a new Notification.
In your sample project, if you remove the following line, the deleteIntent works on a Nexus 4 running 4.4.4:
setLatestInfo(getActivity(), notification, routeId);
I suspect that this call is wiping out your deleteIntent. It may work to re-apply your deleteIntent to the Notification as part of your setLatestInfo() processing.
You must have a different problem because I'm able to receive the deleteintent in several 4.3 and 4.4 emulators.
I wanted to test your "simple" project but it uses Android Studio, so I made my own simpler test.
Steps to reproduce:
-Create an Activity and set the launch mode to singleInstance in the manifest.
-In the handler of a button or menu item, launch a notification:
Intent deleteIntent = new Intent(this, MainActivity.class);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setOnlyAlertOnce(true)
.setContentTitle("Notification delete intent test")
.setContentText("Please dismiss this notification by swipping or deleting it. A Toast will be shown if the deletion intent works.")
.setDeleteIntent(PendingIntent.getActivity(this, 0, deleteIntent, 0))
.setAutoCancel(true)
.build();
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify((int)System.currentTimeMillis(), notification);
-Override onNewIntent to show a toast or log a message when the notification is cancelled:
#Override
public void onNewIntent(Intent intent){
Toast.makeText(this, "Notification deleted!", Toast.LENGTH_LONG).show();
}
To dismiss the notification either swipe or press the clear button. It wont work pressing over it because autocancel is not considered an explicit user action and hence the delete intent wont be delivered.

Categories