android Bluetooth OPP profile server端mimetype判断分析

/ android / 没有评论 / 617浏览

用到的代码文件

/packages/apps/Bluetooth/src/:
com/android/bluetooth/opp/BluetoothOppObexServerSession.java

/frameworks/base/core/java/:
android/webkit/MimeTypeMap.java

/libcore/luni/src/main/java/:
libcore/net/MimeUtils.java

代码执行流程图

bluetooth OPP mimetype 查看原图

代码流程分析

下面的代码基于android O(api 27)。

本文基于MTK(MediaTek)平台基线。

这篇文章主要记录的是Bluetooth opp profile server(接收端)中 mime type判断流程。

当bt接收到一个文件传入的请求时,会进入BluetoothOppObexServerSession.java类中的onPut()方法。

这个方法进行判断当前接收文件的mimetype是否允许,如果允许就返回OBEX_HTTP_OK,如果不允许就返回其它类型的值。

@Override
public int onPut(Operation op) {
    if (D) Log.d(TAG, "onPut " + op.toString());
    HeaderSet request;
    String name, mimeType;
    Long length;

    int obexResponse = ResponseCodes.OBEX_HTTP_OK;

    /**
     * For multiple objects, reject further objects after 
     * user deny the first one
     */
    if (mAccepted == BluetoothShare.USER_CONFIRMATION_DENIED) {
        return ResponseCodes.OBEX_HTTP_FORBIDDEN;
    }

    String destination;
    if (mTransport instanceof BluetoothObexTransport) {
        destination = ((BluetoothObexTransport)mTransport)
                .getRemoteAddress();
    } else {
        destination = "FF:FF:FF:00:00:00";
    }
    boolean isWhitelisted = BluetoothOppManager
            .getInstance(mContext).
            isWhitelisted(destination);

    try {
        boolean pre_reject = false;

        request = op.getReceivedHeader();
        if (V) Constants.logHeader(request);
        name = (String)request.getHeader(HeaderSet.NAME);
        length = (Long)request.getHeader(HeaderSet.LENGTH);
        /*
         * 从请求头中获取发送过来的mime type
         */
        mimeType = (String)request.getHeader(HeaderSet.TYPE);

        if (length == 0) {
            if (D) Log.w(TAG, "length is 0, reject the transfer");
            pre_reject = true;
            obexResponse = ResponseCodes.OBEX_HTTP_LENGTH_REQUIRED;
        }

        if (name == null || name.equals("")) {
            if (D) Log.w(TAG, "name is null or empty, "
                    + "reject the transfer");
            pre_reject = true;
            obexResponse = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
        }

        if (!pre_reject) {
            /* first we look for Mimetype in Android map */
            String extension, type;
            int dotIndex = name.lastIndexOf(".");
            if (dotIndex < 0 && mimeType == null) {
                if (D) Log.w(TAG, "There is no file extension "
                        + "or mime type," 
                        + "reject the transfer");
                pre_reject = true;
                obexResponse = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
            } else {
                /*
                 * 从文件名中截取后缀
                 */
                extension = name.substring(dotIndex + 1).toLowerCase();
                /*
                 * 根据文件名后缀尝试获取对应的mime type
                 */
                MimeTypeMap map = MimeTypeMap.getSingleton();
                type = map.getMimeTypeFromExtension(extension);
                if (V) Log.v(TAG, "Mimetype guessed from extension " 
                        + extension + " is " + type);
                /*
                 * 如果从MimeTypeMap中获取的mime type不为空就使用获取的
                 */
                if (type != null) {
                    mimeType = type;
                } else {
                     /*
                      * 如果从MimeTypeMap中获取的mime type为空且请求中
                      * 传输过来的mime type也为空那么就拒绝。
                      */
                    if (mimeType == null) {
                        if (D) Log.w(TAG, "Can't get mimetype, "
                                + "reject the transfer");
                        pre_reject = true;
                        obexResponse = ResponseCodes
                                .OBEX_HTTP_UNSUPPORTED_TYPE;
                    }
                }
                //将最终的mime type小写
                if (mimeType != null) {
                    mimeType = mimeType.toLowerCase();
                }
            }
        }

        /*
         * 判断mime type 是否处在:
         *   Constants.ACCEPTABLE_SHARE_INBOUND_TYPES
         *   Constants.UNACCEPTABLE_SHARE_INBOUND_TYPES
         * 这两个字符串数组中
         */
        // Reject policy: anything outside the "white list" 
        // plus unspecified
        // MIME Types. Also reject everything in the "black list".
        if (!pre_reject
                && (mimeType == null
                || (!isWhitelisted 
                && !Constants.mimeTypeMatches(mimeType,
                Constants.ACCEPTABLE_SHARE_INBOUND_TYPES))
                || Constants.mimeTypeMatches(mimeType,
                Constants.UNACCEPTABLE_SHARE_INBOUND_TYPES))) {
            if (D) Log.w(TAG, "mimeType is null or in "
                  + "unacceptable list, reject the transfer");
            pre_reject = true;
            obexResponse = ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE;
        }

        if (pre_reject 
                && obexResponse != ResponseCodes.OBEX_HTTP_OK) {
            // some bad implemented client won't send disconnect
            return obexResponse;
        }

    } catch (IOException e) {
        Log.e(TAG, "get getReceivedHeaders error " + e);
        return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
    }

    ......(省略一大段代码)

    return obexResponse;
}

下面看下MimeTypeMap.java中的getMimeTypeFromExtension()方法。

public String getMimeTypeFromExtension(String extension) {
    return MimeUtils.guessMimeTypeFromExtension(extension);
}

下面进入MimeUtils.java中的guessMimeTypeFromExtension()方法。

public static String guessMimeTypeFromExtension(String extension) {
    if (extension == null || extension.isEmpty()) {
        return null;
    }
    extension = extension.toLowerCase(Locale.US);
    /*
      * 包含了各种各样的mime type 数组
      */
    return extensionToMimeTypeMap.get(extension);
}

扩展

MimeTypeMap.java这个类在第三方应用中也可以调用。
MimeUtils.java类第三方应用不能使用。