Commit 007b626e authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

WebLayer: Remove media stream notifications from prior app instances.

When the app crashes, it can leave behind media streaming notifications.
Track these in a pref and remove on next startup.

We could use getActiveNotifications instead of storing a pref, but it's
only available in API 23.

Testing is still a todo and may be somewhat difficult as full
integration testing requires crashing the app. For now this is manually
tested by removing the cancel() calls that execute on stream/tab
destruction and restarting the app.

Bug: 1025622
Change-Id: Icdd475707ac1af0789968264c202e00fd8964d33
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2142394
Commit-Queue: Evan Stade <estade@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#757944}
parent 4568e176
...@@ -8,6 +8,7 @@ import android.app.NotificationChannel; ...@@ -8,6 +8,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.AndroidRuntimeException; import android.util.AndroidRuntimeException;
...@@ -21,11 +22,12 @@ import org.chromium.base.annotations.JNINamespace; ...@@ -21,11 +22,12 @@ import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.NativeMethods;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import java.util.HashSet;
import java.util.Set;
/** /**
* A per-tab object that manages notifications for ongoing media streams (microphone/camera). This * A per-tab object that manages notifications for ongoing media streams (microphone/camera). This
* object is created by {@link TabImpl} and creates and destroys its native equivalent. * object is created by {@link TabImpl} and creates and destroys its native equivalent.
*
* TODO(estade): remove notifications that have persisted across restarts (due to the app crashing).
*/ */
@JNINamespace("weblayer") @JNINamespace("weblayer")
public class MediaStreamManager { public class MediaStreamManager {
...@@ -36,6 +38,15 @@ public class MediaStreamManager { ...@@ -36,6 +38,15 @@ public class MediaStreamManager {
private static final String ACTIVATE_TAB_INTENT = WEBRTC_PREFIX + ".ACTIVATE_TAB"; private static final String ACTIVATE_TAB_INTENT = WEBRTC_PREFIX + ".ACTIVATE_TAB";
private static final String AV_STREAM_TAG = WEBRTC_PREFIX + ".avstream"; private static final String AV_STREAM_TAG = WEBRTC_PREFIX + ".avstream";
/**
* A key used in the app's shared preferences to track a set of active streaming notifications.
* This is used to clear notifications that may have persisted across restarts due to a crash.
* TODO(estade): remove this approach and simply iterate across all notifications via
* {@link NotificationManager#getActiveNotifications} once the minimum API level is 23.
*/
private static final String PREF_ACTIVE_AV_STREAM_NOTIFICATION_IDS =
WEBRTC_PREFIX + ".avstream_notifications";
// The notification ID matches the tab ID, which uniquely identifies the notification when // The notification ID matches the tab ID, which uniquely identifies the notification when
// paired with the tag. // paired with the tag.
private int mNotificationId; private int mNotificationId;
...@@ -67,17 +78,62 @@ public class MediaStreamManager { ...@@ -67,17 +78,62 @@ public class MediaStreamManager {
} }
} }
/**
* To be called when WebLayer is started. Clears notifications that may have persisted from
* before a crash.
*/
public static void onWebLayerInit() {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
Set<String> staleNotificationIds =
prefs.getStringSet(PREF_ACTIVE_AV_STREAM_NOTIFICATION_IDS, null);
if (staleNotificationIds == null) return;
NotificationManagerCompat manager = getNotificationManager();
if (manager == null) return;
for (String id : staleNotificationIds) {
manager.cancel(AV_STREAM_TAG, Integer.parseInt(id));
}
prefs.edit().remove(PREF_ACTIVE_AV_STREAM_NOTIFICATION_IDS).apply();
}
public MediaStreamManager(TabImpl tab) { public MediaStreamManager(TabImpl tab) {
mNotificationId = tab.getId(); mNotificationId = tab.getId();
mNative = MediaStreamManagerJni.get().create(this, tab.getWebContents()); mNative = MediaStreamManagerJni.get().create(this, tab.getWebContents());
} }
public void destroy() { public void destroy() {
cancelNotification();
MediaStreamManagerJni.get().destroy(mNative);
}
private void cancelNotification() {
NotificationManagerCompat notificationManager = getNotificationManager(); NotificationManagerCompat notificationManager = getNotificationManager();
if (notificationManager == null) return; if (notificationManager != null) {
notificationManager.cancel(AV_STREAM_TAG, mNotificationId); notificationManager.cancel(AV_STREAM_TAG, mNotificationId);
}
updateActiveNotifications(false);
}
MediaStreamManagerJni.get().destroy(mNative); /**
* Updates the list of active notifications stored in the SharedPrefences.
*
* @param active if true, then {@link mNotificationId} will be added to the list of active
* notifications, otherwise it will be removed.
*/
private void updateActiveNotifications(boolean active) {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
Set<String> activeIds = new HashSet<String>(
prefs.getStringSet(PREF_ACTIVE_AV_STREAM_NOTIFICATION_IDS, new HashSet<String>()));
if (active) {
activeIds.add(Integer.toString(mNotificationId));
} else {
activeIds.remove(Integer.toString(mNotificationId));
}
prefs.edit()
.putStringSet(PREF_ACTIVE_AV_STREAM_NOTIFICATION_IDS,
activeIds.isEmpty() ? null : activeIds)
.apply();
} }
/** /**
...@@ -94,15 +150,15 @@ public class MediaStreamManager { ...@@ -94,15 +150,15 @@ public class MediaStreamManager {
// The notification intent is not handled in the client prior to M84. // The notification intent is not handled in the client prior to M84.
if (WebLayerFactoryImpl.getClientMajorVersion() < 84) return; if (WebLayerFactoryImpl.getClientMajorVersion() < 84) return;
NotificationManagerCompat notificationManager = getNotificationManager();
if (notificationManager == null) return;
createNotificationChannel();
if (!audio && !video) { if (!audio && !video) {
notificationManager.cancel(AV_STREAM_TAG, mNotificationId); cancelNotification();
return; return;
} }
NotificationManagerCompat notificationManager = getNotificationManager();
if (notificationManager == null) return;
createNotificationChannel();
Intent intent = WebLayerImpl.createIntent(); Intent intent = WebLayerImpl.createIntent();
intent.putExtra(EXTRA_TAB_ID, mNotificationId); intent.putExtra(EXTRA_TAB_ID, mNotificationId);
intent.setAction(ACTIVATE_TAB_INTENT); intent.setAction(ACTIVATE_TAB_INTENT);
...@@ -120,6 +176,7 @@ public class MediaStreamManager { ...@@ -120,6 +176,7 @@ public class MediaStreamManager {
? "all the streamz" ? "all the streamz"
: audio ? "audio streamz" : "video streamz"); : audio ? "audio streamz" : "video streamz");
notificationManager.notify(AV_STREAM_TAG, mNotificationId, builder.build()); notificationManager.notify(AV_STREAM_TAG, mNotificationId, builder.build());
updateActiveNotifications(true);
} }
private void createNotificationChannel() { private void createNotificationChannel() {
...@@ -136,7 +193,7 @@ public class MediaStreamManager { ...@@ -136,7 +193,7 @@ public class MediaStreamManager {
sCreatedChannel = true; sCreatedChannel = true;
} }
private NotificationManagerCompat getNotificationManager() { private static NotificationManagerCompat getNotificationManager() {
if (ContextUtils.getApplicationContext() == null) { if (ContextUtils.getApplicationContext() == null) {
return null; return null;
} }
......
...@@ -259,6 +259,8 @@ public final class WebLayerImpl extends IWebLayer.Stub { ...@@ -259,6 +259,8 @@ public final class WebLayerImpl extends IWebLayer.Stub {
LibraryLoader.getInstance().ensureInitialized(); LibraryLoader.getInstance().ensureInitialized();
} }
GmsBridge.getInstance().setSafeBrowsingHandler(); GmsBridge.getInstance().setSafeBrowsingHandler();
MediaStreamManager.onWebLayerInit();
} }
@Override @Override
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment