Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2fe3af04 authored by Sal Savage's avatar Sal Savage
Browse files

Keep Media Framework/Bitmap exceptions from crashing Bluetooth

In some rare cases, Media Framework classes can fail to unparcel and
throw a runtime exception. These result in the calling application
crashing, whether its Bluetooth, the Notification Bar, etc.

This change wraps our calls to access the Media Framework classes' data
in try-catch blocks and returns empty data/empty lists when something
goes wrong. We're logging an error message and the paired exception when
it happens as well so we can still monitor for the problem.

This means the remote device will see the default "Not Provided" when
something goes wrong with the current track, and an empty queue or
folder when something goes wrong with the browsed items.

This is not meant to be the final solution, only a stand in to prevent
crashes until a formal solution can be determined in the framework we're
calling into.

Tag: #stability
Bug: 191525560
Test: atest BluetoothInstrumentationTests
Change-Id: I82d006decd49032c9d166810e20f129538b106e3
parent 150dd7d4
Loading
Loading
Loading
Loading
+6 −1
Original line number Original line Diff line number Diff line
@@ -429,7 +429,12 @@ class BrowsedPlayerWrapper {
                    Folder f = new Folder(item.getMediaId(), false, title);
                    Folder f = new Folder(item.getMediaId(), false, title);
                    return_list.add(new ListItem(f));
                    return_list.add(new ListItem(f));
                } else {
                } else {
                    return_list.add(new ListItem(Util.toMetadata(mContext, item)));
                    Metadata data = Util.toMetadata(mContext, item);
                    if (Util.isEmptyData(data)) {
                        Log.e(TAG, "Received empty Metadata, ignoring browsed item");
                        continue;
                    }
                    return_list.add(new ListItem(data));
                }
                }
            }
            }


+49 −18
Original line number Original line Diff line number Diff line
@@ -43,17 +43,16 @@ class Util {
     * Get an empty set of Metadata
     * Get an empty set of Metadata
     */
     */
    public static final Metadata empty_data() {
    public static final Metadata empty_data() {
        Metadata ret = new Metadata();
        Metadata.Builder builder = new Metadata.Builder();
        ret.mediaId = "Not Provided";
        return builder.useDefaults().build();
        ret.title = "Not Provided";
    }
        ret.artist = "";

        ret.album = "";
    /**
        ret.genre = "";
     * Determine if a set of Metadata is "empty" as defined by audio_util.
        ret.trackNum = "1";
     */
        ret.numTracks = "1";
    public static final boolean isEmptyData(Metadata data) {
        ret.duration = "0";
        if (data == null) return true;
        ret.image = null;
        return (empty_data().equals(data) && data.mediaId.equals("Not Provided"));
        return ret;
    }
    }


    /**
    /**
@@ -71,7 +70,12 @@ class Util {
     */
     */
    public static Metadata toMetadata(Context context, Bundle bundle) {
    public static Metadata toMetadata(Context context, Bundle bundle) {
        Metadata.Builder builder = new Metadata.Builder();
        Metadata.Builder builder = new Metadata.Builder();
        try {
            return builder.useContext(context).useDefaults().fromBundle(bundle).build();
            return builder.useContext(context).useDefaults().fromBundle(bundle).build();
        } catch (Exception e) {
            Log.e(TAG, "Failed to build Metadata from Bundle, returning empty data", e);
            return empty_data();
        }
    }
    }


    /**
    /**
@@ -86,8 +90,13 @@ class Util {
        }
        }


        Metadata.Builder builder = new Metadata.Builder();
        Metadata.Builder builder = new Metadata.Builder();
        try {
            return builder.useContext(context).useDefaults().fromMediaDescription(desc)
            return builder.useContext(context).useDefaults().fromMediaDescription(desc)
                    .fromMediaMetadata(data).build();
                    .fromMediaMetadata(data).build();
        } catch (Exception e) {
            Log.e(TAG, "Failed to build Metadata from MediaDescription, returning empty data", e);
            return empty_data();
        }
    }
    }


    /**
    /**
@@ -95,14 +104,27 @@ class Util {
     */
     */
    public static Metadata toMetadata(Context context, MediaItem item) {
    public static Metadata toMetadata(Context context, MediaItem item) {
        Metadata.Builder builder = new Metadata.Builder();
        Metadata.Builder builder = new Metadata.Builder();
        try {
            return builder.useContext(context).useDefaults().fromMediaItem(item).build();
            return builder.useContext(context).useDefaults().fromMediaItem(item).build();
        } catch (Exception e) {
            Log.e(TAG, "Failed to build Metadata from MediaItem, returning empty data", e);
            return empty_data();
        }
    }
    }


    /**
    /**
     * Translate a MediaSession.QueueItem to audio_util's Metadata
     * Translate a MediaSession.QueueItem to audio_util's Metadata
     */
     */
    public static Metadata toMetadata(Context context, MediaSession.QueueItem item) {
    public static Metadata toMetadata(Context context, MediaSession.QueueItem item) {
        Metadata.Builder builder = new Metadata.Builder().useDefaults().fromQueueItem(item);
        Metadata.Builder builder = new Metadata.Builder();

        try {
            builder.useDefaults().fromQueueItem(item);
        } catch (Exception e) {
            Log.e(TAG, "Failed to build Metadata from QueueItem, returning empty data", e);
            return empty_data();
        }

        // For Queue Items, the Media Id will always be just its Queue ID
        // For Queue Items, the Media Id will always be just its Queue ID
        // We don't need to use its actual ID since we don't promise UIDS being valid
        // We don't need to use its actual ID since we don't promise UIDS being valid
        // between a file system and it's now playing list.
        // between a file system and it's now playing list.
@@ -118,8 +140,13 @@ class Util {
        // This will always be currsong. The AVRCP service will overwrite the mediaId if it needs to
        // This will always be currsong. The AVRCP service will overwrite the mediaId if it needs to
        // TODO (apanicke): Remove when the service is ready, right now it makes debugging much more
        // TODO (apanicke): Remove when the service is ready, right now it makes debugging much more
        // convenient
        // convenient
        try {
            return builder.useContext(context).useDefaults().fromMediaMetadata(data)
            return builder.useContext(context).useDefaults().fromMediaMetadata(data)
                    .setMediaId("currsong").build();
                    .setMediaId("currsong").build();
        } catch (Exception e) {
            Log.e(TAG, "Failed to build Metadata from MediaMetadata, returning empty data", e);
            return empty_data();
        }
    }
    }


    /**
    /**
@@ -133,6 +160,10 @@ class Util {


        for (int i = 0; i < items.size(); i++) {
        for (int i = 0; i < items.size(); i++) {
            Metadata data = toMetadata(context, items.get(i));
            Metadata data = toMetadata(context, items.get(i));
            if (isEmptyData(data)) {
                Log.e(TAG, "Received an empty Metadata item in list. Returning an empty queue");
                return new ArrayList<Metadata>();
            }
            data.trackNum = "" + (i + 1);
            data.trackNum = "" + (i + 1);
            data.numTracks = "" + items.size();
            data.numTracks = "" + items.size();
            list.add(data);
            list.add(data);