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

Commit 867daa7d authored by Android Build Merger (Role)'s avatar Android Build Merger (Role) Committed by Android (Google) Code Review
Browse files

Merge "Merge "Move default MimeMap implementation to frameworks." am:...

Merge "Merge "Move default MimeMap implementation to frameworks." am: 19c3bdc8 am: 3f1d5d92 am: 1b6d7183 am: 4999ce08" into qt-qpr1-dev-plus-aosp
parents cb7ebeec 011723e2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -177,6 +177,7 @@ java_defaults {
    jarjar_rules: ":framework-jarjar-rules",

    static_libs: [
        "mimemap",
        "apex_aidl_interface-java",
        "suspend_control_aidl_interface-java",
        "framework-protos",
+9 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
import android.content.type.MimeMapImpl;
import android.os.Build;
import android.os.DeadObjectException;
import android.os.Debug;
@@ -33,6 +34,9 @@ import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
import dalvik.system.RuntimeHooks;
import dalvik.system.VMRuntime;

import libcore.net.MimeMap;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -209,6 +213,11 @@ public class RuntimeInit {
         */
        RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));

        /*
         * Set a default mapping between MIME types and file extensions.
         */
        MimeMap.setDefault(MimeMapImpl.createDefaultInstance());

        /*
         * Sets handler for java.util.logging to use Android log facilities.
         * The odd "new instance-and-then-throw-away" is a mirror of how

mime/Android.bp

0 → 100644
+43 −0
Original line number Diff line number Diff line
// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

java_library {
    name: "mimemap",
    visibility: [
        "//cts/tests/tests/mimemap:__subpackages__",
        "//frameworks/base:__subpackages__",
    ],

    srcs: [
        "java/android/content/type/MimeMapImpl.java",
    ],

    java_resources: [
        ":debian.mime.types",
        ":android.mime.types",
    ],

    sdk_version: "core_platform",
}

filegroup {
    name: "android.mime.types",
    visibility: [
        "//visibility:private",
    ],
    path: "java-res/",
    srcs: [
        "java-res/android.mime.types",
    ],
}
+146 −0
Original line number Diff line number Diff line

###############################################################################
#
# Android-specific MIME type <-> extension mappings
#
# Each line below defines an mapping from one MIME type to the first of the
# listed extensions, and from listed extension back to the MIME type.
# A mapping overrides any previous mapping _from_ that same MIME type or
# extension (put() semantics), unless that MIME type / extension is prefixed with '?'
# (putIfAbsent() semantics).
#
#
###############################################################################
#
# EXAMPLES
#
# A line of the form:
#
#    ?mime ext1 ?ext2 ext3
#
# affects the current mappings along the lines of the following pseudo code:
#
#    mimeToExt.putIfAbsent("mime", "ext1");
#    extToMime.put("ext1", "mime");
#    extToMime.putIfAbsent("ext2", "mime");
#    extToMime.put("ext3", "mime");
#
# The line:
#
#     ?text/plain txt
#
# leaves any earlier mapping for "text/plain" untouched, or maps that MIME type
# to the file extension ".txt" if there is no earlier mapping. The line also
# sets the mapping from file extension ".txt" to be the MIME type "text/plain",
# regardless of whether a previous mapping existed.
#
###############################################################################


# File extensions that Android wants to override to point to the given MIME type.
#
# After processing a line of the form:
# ?<mimeType> <extension1> <extension2>
# If <mimeType> was not already mapped to an extension then it will be
# mapped to <extension1>.
# <extension1> and <extension2> are mapped (or remapped) to <mimeType>.

?application/epub+zip epub
?application/pkix-cert cer
?application/rss+xml rss
?application/vnd.android.ota ota
?application/vnd.apple.mpegurl m3u8
?application/vnd.ms-pki.stl stl
?application/vnd.ms-powerpoint pot
?application/vnd.ms-wpl wpl
?application/vnd.stardivision.impress sdp
?application/vnd.stardivision.writer vor
?application/vnd.youtube.yt yt
?application/x-android-drm-fl fl
?application/x-flac flac
?application/x-font pcf
?application/x-mpegurl m3u m3u8
?application/x-pem-file pem
?application/x-pkcs12 p12 pfx
?application/x-webarchive webarchive
?application/x-webarchive-xml webarchivexml
?application/x-x509-server-cert crt
?application/x-x509-user-cert crt

?audio/3gpp 3gpp
?audio/aac-adts aac
?audio/imelody imy
?audio/midi rtttl xmf
?audio/mobile-xmf mxmf
?audio/mp4 m4a
?audio/mpegurl m3u
?audio/sp-midi smf
?audio/x-matroska mka
?audio/x-pn-realaudio ra

?image/bmp bmp
?image/heic heic
?image/heic-sequence heics
?image/heif heif hif
?image/heif-sequence heifs
?image/ico cur
?image/webp webp
?image/x-adobe-dng dng
?image/x-fuji-raf raf
?image/x-icon ico
?image/x-nikon-nrw nrw
?image/x-panasonic-rw2 rw2
?image/x-pentax-pef pef
?image/x-samsung-srw srw
?image/x-sony-arw arw

?text/comma-separated-values csv
?text/plain diff po
?text/rtf rtf
?text/text phps
?text/xml xml
?text/x-vcard vcf

?video/3gpp2 3gpp2 3g2
?video/3gpp 3gpp
?video/avi avi
?video/m4v m4v
?video/mp2p mpeg
?video/mp2t m2ts mts
?video/mp2ts ts
?video/vnd.youtube.yt yt
?video/x-webex wrf

# Optional additions that should not override any previous mapping.

?application/x-wifi-config ?xml

# Special cases where Android has a strong opinion about mappings, so we
# define them very last and make them override in both directions (no "?").
#
# Lines here are of the form:
# <mimeType> <extension1> <extension2> ...
#
# After processing each line,
#   <mimeType> is mapped to <extension1>
#   <extension1>, <extension2>, ... are all mapped to <mimeType>
# This overrides any mappings for this <mimeType> / for these extensions
# that may have been defined earlier.

application/pgp-signature pgp
application/x-x509-ca-cert crt
audio/aac aac
audio/basic snd
audio/flac flac
audio/midi rtx
audio/mpeg mp3 m4a m4r
audio/x-mpegurl m3u m3u8
image/jpeg jpg
image/x-ms-bmp bmp
text/plain txt
text/x-c++hdr hpp
text/x-c++src cpp
video/3gpp 3gpp
video/mpeg mpeg
video/quicktime mov
video/x-matroska mkv
+194 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content.type;

import libcore.net.MimeMap;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * Default implementation of {@link MimeMap}, a bidirectional mapping between
 * MIME types and file extensions.
 *
 * This default mapping is loaded from data files that start with some mappings
 * recognized by IANA plus some custom extensions and overrides.
 *
 * @hide
 */
public class MimeMapImpl extends MimeMap {

    /**
     * Creates and returns a new {@link MimeMapImpl} instance that implements.
     * Android's default mapping between MIME types and extensions.
     */
    public static MimeMapImpl createDefaultInstance() {
        return parseFromResources("/mime.types", "/android.mime.types");
    }

    private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+");

    /**
     * Note: These maps only contain lowercase keys/values, regarded as the
     * {@link #toLowerCase(String) canonical form}.
     *
     * <p>This is the case for both extensions and MIME types. The mime.types
     * data file contains examples of mixed-case MIME types, but some applications
     * use the lowercase version of these same types. RFC 2045 section 2 states
     * that MIME types are case insensitive.
     */
    private final Map<String, String> mMimeTypeToExtension;
    private final Map<String, String> mExtensionToMimeType;

    public MimeMapImpl(Map<String, String> mimeTypeToExtension,
            Map<String, String> extensionToMimeType) {
        this.mMimeTypeToExtension = new HashMap<>(mimeTypeToExtension);
        for (Map.Entry<String, String> entry : mimeTypeToExtension.entrySet()) {
            checkValidMimeType(entry.getKey());
            checkValidExtension(entry.getValue());
        }
        this.mExtensionToMimeType = new HashMap<>(extensionToMimeType);
        for (Map.Entry<String, String> entry : extensionToMimeType.entrySet()) {
            checkValidExtension(entry.getKey());
            checkValidMimeType(entry.getValue());
        }
    }

    private static void checkValidMimeType(String s) {
        if (MimeMap.isNullOrEmpty(s) || !s.equals(MimeMap.toLowerCase(s))) {
            throw new IllegalArgumentException("Invalid MIME type: " + s);
        }
    }

    private static void checkValidExtension(String s) {
        if (MimeMap.isNullOrEmpty(s) || !s.equals(MimeMap.toLowerCase(s))) {
            throw new IllegalArgumentException("Invalid extension: " + s);
        }
    }

    static MimeMapImpl parseFromResources(String... resourceNames) {
        Map<String, String> mimeTypeToExtension = new HashMap<>();
        Map<String, String> extensionToMimeType = new HashMap<>();
        for (String resourceName : resourceNames) {
            parseTypes(mimeTypeToExtension, extensionToMimeType, resourceName);
        }
        return new MimeMapImpl(mimeTypeToExtension, extensionToMimeType);
    }

    /**
     * An element of a *mime.types file: A MIME type or an extension, with an optional
     * prefix of "?" (if not overriding an earlier value).
     */
    private static class Element {
        public final boolean keepExisting;
        public final String s;

        Element(boolean keepExisting, String value) {
            this.keepExisting = keepExisting;
            this.s = toLowerCase(value);
            if (value.isEmpty()) {
                throw new IllegalArgumentException();
            }
        }

        public String toString() {
            return keepExisting ? ("?" + s) : s;
        }
    }

    private static String maybePut(Map<String, String> map, Element keyElement, String value) {
        if (keyElement.keepExisting) {
            return map.putIfAbsent(keyElement.s, value);
        } else {
            return map.put(keyElement.s, value);
        }
    }

    private static void parseTypes(Map<String, String> mimeTypeToExtension,
            Map<String, String> extensionToMimeType, String resource) {
        try (BufferedReader r = new BufferedReader(
                new InputStreamReader(MimeMapImpl.class.getResourceAsStream(resource)))) {
            String line;
            while ((line = r.readLine()) != null) {
                int commentPos = line.indexOf('#');
                if (commentPos >= 0) {
                    line = line.substring(0, commentPos);
                }
                line = line.trim();
                // The first time a MIME type is encountered it is mapped to the first extension
                // listed in its line. The first time an extension is encountered it is mapped
                // to the MIME type.
                //
                // When encountering a previously seen MIME type or extension, then by default
                // the later ones override earlier mappings (put() semantics); however if a MIME
                // type or extension is prefixed with '?' then any earlier mapping _from_ that
                // MIME type / extension is kept (putIfAbsent() semantics).
                final String[] split = SPLIT_PATTERN.split(line);
                if (split.length <= 1) {
                    // Need mimeType + at least one extension to make a mapping.
                    // "mime.types" files may also contain lines with just a mimeType without
                    // an extension but we skip them as they provide no mapping info.
                    continue;
                }
                List<Element> lineElements = new ArrayList<>(split.length);
                for (String s : split) {
                    boolean keepExisting = s.startsWith("?");
                    if (keepExisting) {
                        s = s.substring(1);
                    }
                    if (s.isEmpty()) {
                        throw new IllegalArgumentException("Invalid entry in '" + line + "'");
                    }
                    lineElements.add(new Element(keepExisting, s));
                }

                // MIME type -> first extension (one mapping)
                // This will override any earlier mapping from this MIME type to another
                // extension, unless this MIME type was prefixed with '?'.
                Element mimeElement = lineElements.get(0);
                List<Element> extensionElements = lineElements.subList(1, lineElements.size());
                String firstExtension = extensionElements.get(0).s;
                maybePut(mimeTypeToExtension, mimeElement, firstExtension);

                // extension -> MIME type (one or more mappings).
                // This will override any earlier mapping from this extension to another
                // MIME type, unless this extension was prefixed with '?'.
                for (Element extensionElement : extensionElements) {
                    maybePut(extensionToMimeType, extensionElement, mimeElement.s);
                }
            }
        } catch (IOException | RuntimeException e) {
            throw new RuntimeException("Failed to parse " + resource, e);
        }
    }

    @Override
    protected String guessExtensionFromLowerCaseMimeType(String mimeType) {
        return mMimeTypeToExtension.get(mimeType);
    }

    @Override
    protected String guessMimeTypeFromLowerCaseExtension(String extension) {
        return mExtensionToMimeType.get(extension);
    }
}