Ensure PendingIntents use explicit Intents.

The only pending intents that were not explicit were the four from
the NotificationHelper class. These now explicitly specify the
NotificationBroadcastReceiver as their destination, which is not
exported. That then forwards the intents onto relevant methods of
AppUpdateStatusManager.
This commit is contained in:
Peter Serwylo 2017-04-20 14:02:01 +10:00
parent 9703589950
commit b0803432d8
4 changed files with 54 additions and 38 deletions

View File

@ -229,6 +229,7 @@
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.PackageManagerReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
@ -238,12 +239,17 @@
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.WifiStateChangeReceiver" >
<intent-filter>
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
<receiver android:name=".NotificationBroadcastReceiver" android:exported="false">
<!-- Doesn't require an intent-filter because it is explicitly invoked via Intent.setClass() -->
</receiver>
<service android:name=".UpdateService" />
<service
android:name=".net.DownloaderService"

View File

@ -145,7 +145,7 @@ public class FDroidApp extends Application {
/**
* Force reload the {@link Activity to make theme changes take effect.}
* Same as {@link Languages.forceChangeLanguage}
* Same as {@link Languages#forceChangeLanguage(Activity)}
*
* @param activity the {@code Activity} to force reload
*/

View File

@ -0,0 +1,38 @@
package org.fdroid.fdroid;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* For security purposes we need to ensure that all Intent objects we give to a PendingIntent are
* explicitly set to be delivered to an F-Droid class.
* This class takes the global intent received from outside our process (i.e. from the
* notification manager) and passes it onto the {@link AppUpdateStatusManager}.
*/
public class NotificationBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(context);
String notificationKey = intent.getStringExtra(NotificationHelper.EXTRA_NOTIFICATION_KEY);
switch (intent.getAction()) {
case NotificationHelper.BROADCAST_NOTIFICATIONS_ALL_UPDATES_CLEARED:
manager.clearAllUpdates();
break;
case NotificationHelper.BROADCAST_NOTIFICATIONS_ALL_INSTALLED_CLEARED:
manager.clearAllInstalled();
break;
case NotificationHelper.BROADCAST_NOTIFICATIONS_UPDATE_CLEARED:
// If clearing apps in state "InstallError" (like when auto-cancelling) we
// remove them from the status manager entirely.
AppUpdateStatusManager.AppUpdateStatus appUpdateStatus = manager.get(notificationKey);
if (appUpdateStatus != null && appUpdateStatus.status == AppUpdateStatusManager.Status.InstallError) {
manager.removeApk(notificationKey);
}
break;
case NotificationHelper.BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED:
manager.removeApk(notificationKey);
break;
}
}
}

View File

@ -36,10 +36,10 @@ import java.util.ArrayList;
class NotificationHelper {
private static final String BROADCAST_NOTIFICATIONS_ALL_UPDATES_CLEARED = "org.fdroid.fdroid.installer.notifications.allupdates.cleared";
private static final String BROADCAST_NOTIFICATIONS_ALL_INSTALLED_CLEARED = "org.fdroid.fdroid.installer.notifications.allinstalled.cleared";
private static final String BROADCAST_NOTIFICATIONS_UPDATE_CLEARED = "org.fdroid.fdroid.installer.notifications.update.cleared";
private static final String BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED = "org.fdroid.fdroid.installer.notifications.installed.cleared";
static final String BROADCAST_NOTIFICATIONS_ALL_UPDATES_CLEARED = "org.fdroid.fdroid.installer.notifications.allupdates.cleared";
static final String BROADCAST_NOTIFICATIONS_ALL_INSTALLED_CLEARED = "org.fdroid.fdroid.installer.notifications.allinstalled.cleared";
static final String BROADCAST_NOTIFICATIONS_UPDATE_CLEARED = "org.fdroid.fdroid.installer.notifications.update.cleared";
static final String BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED = "org.fdroid.fdroid.installer.notifications.installed.cleared";
private static final int NOTIFY_ID_UPDATES = 1;
private static final int NOTIFY_ID_INSTALLED = 2;
@ -47,7 +47,7 @@ class NotificationHelper {
private static final int MAX_UPDATES_TO_SHOW = 5;
private static final int MAX_INSTALLED_TO_SHOW = 10;
private static final String EXTRA_NOTIFICATION_KEY = "key";
static final String EXTRA_NOTIFICATION_KEY = "key";
private static final String GROUP_UPDATES = "updates";
private static final String GROUP_INSTALLED = "installed";
@ -69,39 +69,7 @@ class NotificationHelper {
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
// We need to listen to when notifications are cleared, so that we "forget" all that we currently know about updates
// and installs.
IntentFilter filter = new IntentFilter();
filter.addAction(BROADCAST_NOTIFICATIONS_ALL_UPDATES_CLEARED);
filter.addAction(BROADCAST_NOTIFICATIONS_ALL_INSTALLED_CLEARED);
filter.addAction(BROADCAST_NOTIFICATIONS_UPDATE_CLEARED);
filter.addAction(BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED);
BroadcastReceiver receiverNotificationsCleared = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case BROADCAST_NOTIFICATIONS_ALL_UPDATES_CLEARED:
appUpdateStatusManager.clearAllUpdates();
break;
case BROADCAST_NOTIFICATIONS_ALL_INSTALLED_CLEARED:
appUpdateStatusManager.clearAllInstalled();
break;
case BROADCAST_NOTIFICATIONS_UPDATE_CLEARED:
// If clearing apps in state "InstallError" (like when auto-cancelling) we
// remove them from the status manager entirely.
AppUpdateStatusManager.AppUpdateStatus appUpdateStatus = appUpdateStatusManager.get(intent.getStringExtra(EXTRA_NOTIFICATION_KEY));
if (appUpdateStatus != null && appUpdateStatus.status == AppUpdateStatusManager.Status.InstallError) {
appUpdateStatusManager.removeApk(intent.getStringExtra(EXTRA_NOTIFICATION_KEY));
}
break;
case BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED:
appUpdateStatusManager.removeApk(intent.getStringExtra(EXTRA_NOTIFICATION_KEY));
break;
}
}
};
context.registerReceiver(receiverNotificationsCleared, filter);
filter = new IntentFilter();
filter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_LIST_CHANGED);
filter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_ADDED);
filter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_CHANGED);
@ -371,6 +339,7 @@ class NotificationHelper {
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_UPDATE_CLEARED);
intentDeleted.putExtra(EXTRA_NOTIFICATION_KEY, entry.getUniqueKey());
intentDeleted.setClass(context, NotificationBroadcastReceiver.class);
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setDeleteIntent(piDeleted);
return builder.build();
@ -429,6 +398,7 @@ class NotificationHelper {
}
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_ALL_UPDATES_CLEARED);
intentDeleted.setClass(context, NotificationBroadcastReceiver.class);
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setDeleteIntent(piDeleted);
return builder.build();
@ -456,6 +426,7 @@ class NotificationHelper {
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED);
intentDeleted.putExtra(EXTRA_NOTIFICATION_KEY, entry.getUniqueKey());
intentDeleted.setClass(context, NotificationBroadcastReceiver.class);
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setDeleteIntent(piDeleted);
return builder.build();
@ -501,6 +472,7 @@ class NotificationHelper {
.setGroupSummary(true);
}
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_ALL_INSTALLED_CLEARED);
intentDeleted.setClass(context, NotificationBroadcastReceiver.class);
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setDeleteIntent(piDeleted);
return builder.build();