Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
246 views
in Technique[技术] by (71.8m points)

android - Programmatically Auto Accept Incoming Bluetooth Files

I am trying to find a way to have a tablet basically auto-accept/give permission to accept an incoming Bluetooth Share file transferred from a laptop to my Nexus Android device. Since the Android bluetooth system itself does not support this feature, I was wondering if this could be done programmatically using an Android application that listened for the notification and accepted it for me.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

MUCH EASIER WAY

If you have a rooted device and use XPosed Framework, your goal can be achieved much easier.

You Need not implement your own bluetooth server nor kill the original BT service, which are very bothering!!!

xposed tutorial link.

Try this code.

import android.util.*;
import de.robv.android.xposed.*;
import de.robv.android.xposed.callbacks.XC_LoadPackage.*;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

public class Tutorial implements IXposedHookLoadPackage
{

    private String TAG="TUTORIAL";
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        if (!lpparam.packageName.equals("com.android.bluetooth"))
        {
            Log.i(TAG,"Not: "+lpparam.packageName);
            return;
        }
        Log.i(TAG,"Yes "+lpparam.packageName);  

        findAndHookMethod("com.android.bluetooth.opp.BluetoothOppManager", lpparam.classLoader, "isWhitelisted", String.class,new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    Log.v(TAG,"HOOK DONE");
                    param.setResult(true); /* you can compare the sender address(String) with your computer and determine if you return true or just allow the original method to be called after this returns.*/

                }
            });

    }
}

I tested and it works fine:)

Links

Dropbox link of the auto accepting app

Dropbox link of the project files (zip)

Xposed apk site

Towelroot site to root your phone

Background(Original answer)

As I commented above, you bay be able to, and I tried and succeeded in blocking (though not receiving) with this code.

import android.util.*;
import de.robv.android.xposed.*;
import de.robv.android.xposed.callbacks.XC_LoadPackage.*;
import java.io.*;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

public class Tutorial implements IXposedHookLoadPackage
{

    private String TAG="TUTORIAL";
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        if (!lpparam.packageName.equals("com.android.bluetooth"))
        {
            Log.i(TAG,"Not: "+lpparam.packageName);
            return;
        }
        Log.i(TAG,"Yes "+lpparam.packageName);  

        findAndHookMethod("com.android.bluetooth.opp.BluetoothOppService", lpparam.classLoader, "startSocketListener", new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    Log.v(TAG,"HOOK DONE");
                    param.setResult(null);
                }

            });

    }
}

The code above hooks the method startListenerSocket() of com.android.bluetooth.BluetoothOppService and prevents the original method from being called by the line param.setResult(null);

Refer to here to see the full code of com.android.bluetooth.BluetoothOppService.java and you will understand the operation.

And the code you can start from is shown below.

import android.util.*;
import de.robv.android.xposed.*;
import de.robv.android.xposed.callbacks.XC_LoadPackage.*;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

public class Tutorial implements IXposedHookLoadPackage
{

    private String TAG="TUTORIAL";
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        if (!lpparam.packageName.equals("com.android.bluetooth"))
        {
            Log.i(TAG,"Not: "+lpparam.packageName);
            return;
        }
        Log.i(TAG,"Yes "+lpparam.packageName);  

        findAndHookMethod("com.android.bluetooth.opp.BluetoothOppObexServerSession", lpparam.classLoader, "onPut", new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    Log.v(TAG,"HOOK DONE");
                    Class c=param.thisObject.getClass();
                }

            });

    }
}

This code hooks the onPut method of com.android.bluetooth. BluetoothOppObexServerSession linked here. I either am newbie to xposed framework but I hope my answer helped.

I had the same issues you asked and partially solved the problem by implementing my custom OBEX server and manually / programmatically(with ps|grep and su kill pid) killing the native BluetoothOppService. But I will either try the idea of hooking and directly executing my code.

And to help you customize OBEX server session I post my implementation below.

    @Override
    public int onPut(Operation op)
    {
        if (D)
        {
            Log.d(TAG, "onPut " + op.toString());
        }
        HeaderSet request;
        String name, mimeType;
        Long length;
        String extension=null;// type;
        int obexResponse = ResponseCodes.OBEX_HTTP_OK;

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

        try
        {
            boolean preReject = false;

            request = op.getReceivedHeader();
            if (V)
            {
                // Constants.logHeader(request);
            }
            name = (String) request.getHeader(HeaderSet.NAME);
            length = (Long) request.getHeader(HeaderSet.LENGTH);
            mimeType = (String) request.getHeader(HeaderSet.TYPE);

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

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

            int dotIndex = name.lastIndexOf(".");
            if (dotIndex > 0)
            {
                extension = name.substring(dotIndex + 1).toLowerCase();
            }


            // Reject policy: anything outside the "white list" plus unspecified
            // MIME Types. Also reject everything in the "black list".
//            if (!preReject && (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");
//                }
//                preReject = true;
//                obexResponse = ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE;
//            }

            if (preReject && 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;
        }         
        int status = receiveFile(destination, name, extension, length, op);
        /*
         * TODO map status to obex response code
         */
        if (status != BluetoothShare.STATUS_SUCCESS)
        {
            obexResponse = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
        }
        Log.d(TAG, "MIME TYPE)" + mimeType);
        return obexResponse;
    }

I just removed some rejecting codes from the original one.

Also to look at my full code please refer to my git repository.

I also thank the contributors to the android project!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...