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

Commit 882898bc authored by Yuchao Zhou's avatar Yuchao Zhou Committed by Android Git Automerger
Browse files

am 77ad02fc: am c95668c5: am 88daa997: am c38ffecc: Merge "Moving BTtraffic...

am 77ad02fc: am c95668c5: am 88daa997: am c38ffecc: Merge "Moving BTtraffic from experiment location to here" into cw-d-mr1-dev

* commit '77ad02fc':
  Moving BTtraffic from experiment location to here
parents 21daba33 77ad02fc
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_RESOURCE_DIR := \
    $(LOCAL_PATH)/res

LOCAL_PACKAGE_NAME := bttraffic
LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)

include $(call all-makefiles-under,$(LOCAL_PATH))
+22 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.android.experimental.bttraffic" >

    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>

    <uses-sdk
        android:minSdkVersion="18"
        android:targetSdkVersion="18"
        />
    <application
        android:allowBackup="false"
        android:label="@string/app_name" >
        <service
            android:name=".BTtraffic"
            android:enabled="true"
            android:exported="true" >
        </service>
    </application>

</manifest>
+45 −0
Original line number Diff line number Diff line
This is a tool to generate classic Bluetooth traffic with specified period and package size.
Together with the SvcMonitor, which will be called automatically in this android service, can be
used to measure the CPU usage from the Java layer Bluetooth code and the underlying system service
com.android.bluetooth.

1. Server (Listener) - Client (Sender) model. Both run as an Android service.
2. No pairing needed. Communicate via unsecured RFcomm. Client establishes the connection by
providing the MAC addr of the server.
3. Bluetooth has to be turned on on both side.
4. Client can configure the traffic by specifying the transfer period and package size.
5. A separate monitor process will be automatically forked and will be reading from /proc file
system to calculate the cpu usage. The measurement is updated once per second.
6. The monitor process (com.google.android.experimental.svcmonitor/.ScvMonitor) can be run as an
independent service to measure cpu usage on any similarly configured tests (e.g. wifi, BLE). Refer
to SvcMonitor's README for usage and details.

Usage:
To instal the test:
On both the server and client device, install the 2 apk:
$ adb install $OUT/system/app/bttraffic/bttraffic.apk
$ adb install $OUT/system/app/svcmonitor/svcmonitor.apk

To start the service on the SERVER side:
$ adb shell am startservice -a start --ez ack true \
com.google.android.experimental.bttraffic/.BTtraffic

To start the service on the CLIENT side:
$ adb shell am startservice -a start \
-e addr "F8:A9:D0:A8:74:8E" --ei size 1000 --ei period 15 \
com.google.android.experimental.bttraffic/.BTtraffic

To stop the test:
On either the server or client:
$ adb shell am startservice -a stop \
com.google.android.experimental.bttraffic/.BTtraffic

To look at the data:
$ adb logcat | grep bttraffic

Options:
-e addr: MAC addr of the server, in uppercase letter.
--ei size: package size, unit: byte; default: 1024, MAX: 20MB
--ei period: system sleep time between sending each package, unit: ms, default: 5000
                  ** if -1 is provided, client will only send the package once.
--ez ack: whether acknowledge is required (true/false)
+3 −0
Original line number Diff line number Diff line
<resources>
    <string name="app_name">Bluetooth Test</string>
</resources>
+328 −0
Original line number Diff line number Diff line
package com.google.android.experimental.bttraffic;

import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Exception;
import java.lang.Runtime;
import java.lang.RuntimeException;
import java.lang.Process;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.Set;
import java.util.UUID;

public class BTtraffic extends Service {
    public static final String TAG = "bttraffic";
    static final String SERVICE_NAME = "bttraffic";
    static final String SYS_SERVICE_NAME = "com.android.bluetooth";
    static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67");
    volatile Thread mWorkerThread;
    volatile boolean isShuttingDown = false;
    volatile boolean isServer = false;

    public BTtraffic() {}

    static void safeClose(Closeable closeable) {
        try {
            closeable.close();
        } catch (IOException e) {
            Log.d(TAG, "Unable to close resource.\n");
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            stopSelf();
            return 0;
        }
        if ("stop".equals(intent.getAction())) {
            stopService();
        } else if ("start".equals(intent.getAction())) {
            startWorker(intent);
        } else {
            Log.d(TAG, "unknown action: + " + intent.getAction());
        }
        return 0;
    }

    private void startWorker(Intent intent) {
        if (mWorkerThread != null) {
            Log.d(TAG, "worker thread already active");
            return;
        }
        isShuttingDown = false;
        String remoteAddr = intent.getStringExtra("addr");
        Log.d(TAG, "startWorker: addr=" + remoteAddr);
        Runnable worker =
                remoteAddr == null
                        ? new ListenerRunnable(this, intent)
                        : new SenderRunnable(this, remoteAddr, intent);
        isServer = remoteAddr == null ? true: false;
        mWorkerThread = new Thread(worker, "BTtrafficWorker");
        try {
            startMonitor();
            Log.d(TAG, "Monitor service started");
            mWorkerThread.start();
            Log.d(TAG, "Worker thread started");
        } catch (Exception e) {
            Log.d(TAG, "Failed to start service", e);
        }
    }

    private void startMonitor()
            throws Exception {
        if (isServer) {
            Log.d(TAG, "Start monitor on server");
            String[] startmonitorCmd = {
                    "/system/bin/am",
                    "startservice",
                    "-a", "start",
                    "-e", "java", SERVICE_NAME,
                    "-e", "hal", SYS_SERVICE_NAME,
                    "com.google.android.experimental.svcmonitor/.SvcMonitor"
            };
            Process ps = new ProcessBuilder()
                    .command(startmonitorCmd)
                    .redirectErrorStream(true)
                    .start();
        } else {
            Log.d(TAG, "No need to start SvcMonitor on client");
        }
    }

    private void stopMonitor()
            throws Exception {
        if (isServer) {
            Log.d(TAG, "StopMonitor on server");
            String[] stopmonitorCmd = {
                    "/system/bin/am",
                    "startservice",
                    "-a", "stop",
                    "com.google.android.experimental.svcmonitor/.SvcMonitor"
            };
            Process ps = new ProcessBuilder()
                    .command(stopmonitorCmd)
                    .redirectErrorStream(true)
                    .start();
        } else {
            Log.d(TAG, "No need to stop Svcmonitor on client");
        }
    }

    public void stopService() {
        if (mWorkerThread == null) {
            Log.d(TAG, "no active thread");
            return;
        }

        isShuttingDown = true;

        try {
            stopMonitor();
        } catch (Exception e) {
            Log.d(TAG, "Unable to stop SvcMonitor!", e);
        }

        if (Thread.currentThread() != mWorkerThread) {
            mWorkerThread.interrupt();
            Log.d(TAG, "Interrupting thread");
            try {
                mWorkerThread.join();
            } catch (InterruptedException e) {
                Log.d(TAG, "Unable to join thread!");
            }
        }

        mWorkerThread = null;
        stopSelf();
        Log.d(TAG, "Service stopped");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public static class ListenerRunnable implements Runnable {
        private final BTtraffic bttraffic;
        private final boolean sendAck;
        private Intent intent;
        private final int maxbuffersize = 20 * 1024 * 1024;

        public ListenerRunnable(BTtraffic bttraffic, Intent intent) {
            this.bttraffic = bttraffic;
            this.sendAck = intent.getBooleanExtra("ack", true);
            this.intent = intent;
        }

        @Override
        public void run() {
            BluetoothServerSocket serverSocket;

            try {
                Log.d(TAG, "getting server socket");
                serverSocket = BluetoothAdapter.getDefaultAdapter()
                        .listenUsingInsecureRfcommWithServiceRecord(
                                SERVICE_NAME, SERVICE_UUID);
            } catch (IOException e) {
                Log.d(TAG, "error creating server socket, stopping thread");
                bttraffic.stopService();
                return;
            }

            Log.d(TAG, "got server socket, starting accept loop");
            BluetoothSocket socket = null;
            try {
                Log.d(TAG, "accepting");
                socket = serverSocket.accept();

                if (!Thread.interrupted()) {
                    Log.d(TAG, "accepted, listening");
                    doListening(socket.getInputStream(), socket.getOutputStream());
                    Log.d(TAG, "listen finished");
                }
            } catch (IOException e) {
                Log.d(TAG, "error while accepting or listening", e);
            } finally {
                Log.d(TAG, "Linster interruped");
                Log.d(TAG, "closing socket and stopping service");
                safeClose(serverSocket);
                safeClose(socket);
                if (!bttraffic.isShuttingDown)
                    bttraffic.stopService();
            }

        }

        private void doListening(InputStream inputStream, OutputStream outputStream)
                throws IOException {
            ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize);

            while (!Thread.interrupted()) {
                readBytesIntoBuffer(inputStream, byteBuffer, 4);
                byteBuffer.flip();
                int length = byteBuffer.getInt();
                if (Thread.interrupted())
                    break;
                readBytesIntoBuffer(inputStream, byteBuffer, length);

                if (sendAck)
                    outputStream.write(0x55);
            }
        }

        void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead)
                throws IOException {
            byteBuffer.clear();
            while (true) {
                int position = byteBuffer.position();
                int remaining = numToRead - position;
                if (remaining == 0) {
                    break;
                }
                int count = inputStream.read(byteBuffer.array(), position, remaining);
                if (count < 0) {
                    throw new IOException("read the EOF");
                }
                byteBuffer.position(position + count);
            }
        }
    }

    public static class SenderRunnable implements Runnable {
        private final BTtraffic bttraffic;
        private final String remoteAddr;
        private final int pkgsize, period;
        private final int defaultpkgsize = 1024;
        private final int defaultperiod = 5000;
        private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4);

        public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) {
            this.bttraffic = bttraffic;
            this.remoteAddr = remoteAddr;
            this.pkgsize = intent.getIntExtra("size", defaultpkgsize);
            this.period = intent.getIntExtra("period", defaultperiod);
        }

        @Override
        public void run() {
            BluetoothDevice device = null;
            try {
                device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
            } catch (IllegalArgumentException e) {
                Log.d(TAG, "Invalid BT MAC address!\n");
            }
            if (device == null) {
                Log.d(TAG, "can't find matching device, stopping thread and service");
                bttraffic.stopService();
                return;
            }

            BluetoothSocket socket = null;
            try {
                Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr);
                socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID);
                socket.connect();
                Log.d(TAG, "connected, starting to send");
                doSending(socket.getOutputStream());
                Log.d(TAG, "send stopped, stopping service");
            } catch (Exception e) {
                Log.d(TAG, "error while sending", e);
            } finally {
                Log.d(TAG, "finishing, closing thread and service");
                safeClose(socket);
                if (!bttraffic.isShuttingDown)
                    bttraffic.stopService();
            }
        }

        private void doSending(OutputStream outputStream) throws IOException {
            Log.w(TAG, "doSending");
            try {
                Random random = new Random(System.currentTimeMillis());

                byte[] bytes = new byte[pkgsize];
                random.nextBytes(bytes);
                while (!Thread.interrupted()) {
                    writeBytes(outputStream, bytes.length);
                    outputStream.write(bytes, 0, bytes.length);
                    if (period < 0)
                        break;
                    if (period == 0)
                        continue;

                    SystemClock.sleep(period);
                }
                Log.d(TAG, "Sender interrupted");
            } catch (IOException e) {
                Log.d(TAG, "doSending got error", e);
            }
        }

        private static void writeBytes(OutputStream outputStream, int value) throws IOException {
            lengthBuffer.putInt(value);
            lengthBuffer.flip();
            outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit());
        }
    }

}
Loading