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 Diff line number Diff line
@@ -429,7 +429,12 @@ class BrowsedPlayerWrapper {
                    Folder f = new Folder(item.getMediaId(), false, title);
                    return_list.add(new ListItem(f));
                } 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 Diff line number Diff line
@@ -43,17 +43,16 @@ class Util {
     * Get an empty set of Metadata
     */
    public static final Metadata empty_data() {
        Metadata ret = new Metadata();
        ret.mediaId = "Not Provided";
        ret.title = "Not Provided";
        ret.artist = "";
        ret.album = "";
        ret.genre = "";
        ret.trackNum = "1";
        ret.numTracks = "1";
        ret.duration = "0";
        ret.image = null;
        return ret;
        Metadata.Builder builder = new Metadata.Builder();
        return builder.useDefaults().build();
    }

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

    /**
@@ -71,7 +70,12 @@ class Util {
     */
    public static Metadata toMetadata(Context context, Bundle bundle) {
        Metadata.Builder builder = new Metadata.Builder();
        try {
            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();
        try {
            return builder.useContext(context).useDefaults().fromMediaDescription(desc)
                    .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) {
        Metadata.Builder builder = new Metadata.Builder();
        try {
            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
     */
    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
        // 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.
@@ -118,8 +140,13 @@ class Util {
        // 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
        // convenient
        try {
            return builder.useContext(context).useDefaults().fromMediaMetadata(data)
                    .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++) {
            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.numTracks = "" + items.size();
            list.add(data);