I have a application that I got froma freelancing team that uses classical Bluetooth in data exchange. I am in the process of switching it from classical Bluetooth to BLE.
Now there are different fragments each with their own viewmodels, viewmodel factories and their repositories that acts as an intermediary between the viewmodel and the DataStore class that hosts the data.
I was following a guide and what I achieved so far is scanning for BLE devices and connecting to them to display the characteristics and their properties (Readable, Writable, Indicatable, Notifiable). I have an activity that scans for devices and then upon selecting a device it goes to another activity, connects to the device and displays the characteristics.
Different Parameters are symbolized with codes likes these:
enum class ReadRequestCodes(val value:String) {
KEY_ADDRESS ("08 00 00 00 20 30 05 11 00 00 00 00 00"),
TOOL_ADDRESS ("08 00 00 00 20 30 05 27 00 00 00 00 00"),
RPM_THRESHOLD("08 00 00 00 20 30 05 13 00 00 00 00 00"),
BACKLASH ("08 00 00 00 20 30 05 22 00 00 00 00 00"),
POWER_SRC_TYPE ("08 00 00 00 20 30 05 26 00 00 00 00 00"),
BATTERY1_PERCENTAGE("08 00 00 00 20 30 11 00 00 00 00 00 00"),
BATTERY2_PERCENTAGE("08 00 00 00 20 30 12 00 00 00 00 00 00"),
HOME_POSITION ("08 00 00 00 20 30 05 15 00 00 00 00 00"),
BYPASS_POSITION ("08 00 00 00 20 30 05 17 00 00 00 00 00"),
HC_POSITION ("08 00 00 00 20 30 05 19 00 00 00 00 00"),
ISOLATION_POSITION("08 00 00 00 20 30 05 1B 00 00 00 00 00"),
PRESSURE_SENSOR_GAIN("08 00 00 00 20 30 05 2B 00 00 00 00 00"),
PRESSURE_SENSOR_OFFSET("08 00 00 00 20 30 05 2C 00 00 00 00 00"),
PRESSURE_SENSOR_RANGE("08 00 00 00 20 30 05 2A 00 00 00 00 00"),
PRESSURE_SENSOR_EXCITATION("08 00 00 00 20 30 05 29 00 00 00 00 00"),
PRESSURE_SENSOR_SERIAL("08 00 00 00 20 30 05 2D 00 00 00 00 00"),
}
there are enums for read requests, write commands, read response.
Now in the fragments previously mentioned there are buttons that are supposed to initiate a request, basically you click and it requests the parameter that corresponds to one of the codes like in the snippet and acquires the value from the device I'm connected to. In other cases I type in data and send it to the device, and the parameter is symbolized with a code similar to the one in the snippet but for write commands.
Now the issue here is HOW to do I exchange data? The code below is form my call back function when I connect to a device:
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
val deviceAddress = gatt.device.address
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Timber.w("onConnectionStateChange: connected to $deviceAddress")
deviceGattMap[gatt.device] = gatt
Handler(Looper.getMainLooper()).post {
gatt.discoverServices()
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Timber.e("onConnectionStateChange: disconnected from $deviceAddress")
teardownConnection(gatt.device)
}
} else {
Timber.e("onConnectionStateChange: status $status encountered for $deviceAddress!")
if (pendingOperation is Connect) {
signalEndOfOperation()
}
teardownConnection(gatt.device)
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
with(gatt) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Timber.w("Discovered ${services.size} services for ${device.address}.")
printGattTable()
requestMtu(device, GATT_MAX_MTU_SIZE)
listeners.forEach { it.get()?.onConnectionSetupComplete?.invoke(this) }
} else {
Timber.e("Service discovery failed due to status $status")
teardownConnection(gatt.device)
}
}
if (pendingOperation is Connect) {
signalEndOfOperation()
}
}
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
Timber.w("ATT MTU changed to $mtu, success: ${status == BluetoothGatt.GATT_SUCCESS}")
listeners.forEach { it.get()?.onMtuChanged?.invoke(gatt.device, mtu) }
if (pendingOperation is MtuRequest) {
signalEndOfOperation()
}
}
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
with(characteristic) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Timber.i("Read characteristic $uuid | value: ${value.toHexString()}")
listeners.forEach {
it.get()?.onCharacteristicRead?.invoke(
gatt.device,
this
)
}
}
BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
Timber.e("Read not permitted for $uuid!")
}
else -> {
Timber.e("Characteristic read failed for $uuid, error: $status")
}
}
}
if (pendingOperation is CharacteristicRead) {
signalEndOfOperation()
}
}
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
with(characteristic) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Timber.i("Wrote to characteristic $uuid | value: ${value.toHexString()}")
listeners.forEach {
it.get()?.onCharacteristicWrite?.invoke(
gatt.device,
this
)
}
}
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> {
Timber.e("Write not permitted for $uuid!")
}
else -> {
Timber.e("Characteristic write failed for $uuid, error: $status")
}
}
}
if (pendingOperation is CharacteristicWrite) {
signalEndOfOperation()
}
}
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic
) {
with(characteristic) {
Timber.i("Characteristic $uuid changed | value: ${value.toHexString()}")
listeners.forEach { it.get()?.onCharacteristicChanged?.invoke(gatt.device, this) }
}
}
override fun onDescriptorRead(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
status: Int
) {
with(descriptor) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Timber.i("Read descriptor $uuid | value: ${value.toHexString()}")
listeners.forEach { it.get()?.onDescriptorRead?.invoke(gatt.device, this) }
}
BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
Timber.e("Read not permitted for $uuid!")
}
else -> {
Timber.e("Descriptor read failed for $uuid, error: $status")
}
}
}
if (pendingOperation is DescriptorRead) {
signalEndOfOperation()
}
}
override fun onDescriptorWrite(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
status: Int
) {
with(descriptor) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Timber.i("Wrote to descriptor $uuid | value: ${value.toHexString()}")
if (isCccd()) {
onCccdWrite(gatt, value, characteristic)
} else {
listeners.forEach {
it.get()?.onDescriptorWrite?.invoke(
gatt.device,
this
)
}
}
}
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> {
Timber.e("Write not permitted for $uuid!")
}
else -> {
Timber.e("Descriptor write failed for $uuid, error: $status")
}
}
}
if (descriptor.isCccd() &&
(pendingOperation is EnableNotifications || pendingOperation is DisableNotifications)
) {
signalEndOfOperation()
} else if (!descriptor.isCccd() && pendingOperation is DescriptorWrite) {
signalEndOfOperation()
}
}
private fun onCccdWrite(
gatt: BluetoothGatt,
value: ByteArray,
characteristic: BluetoothGattCharacteristic
) {
val charUuid = characteristic.uuid
val notificationsEnabled =
value.contentEquals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) ||
value.contentEquals(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
val notificationsDisabled =
value.contentEquals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)
when {
notificationsEnabled -> {
Timber.w("Notifications or indications ENABLED on $charUuid")
listeners.forEach {
it.get()?.onNotificationsEnabled?.invoke(
gatt.device,
characteristic
)
}
}
notificationsDisabled -> {
Timber.w("Notifications or indications DISABLED on $charUuid")
listeners.forEach {
it.get()?.onNotificationsDisabled?.invoke(
gatt.device,
characteristic
)
}
}
else -> {
Timber.e("Unexpected value ${value.toHexString()} on CCCD of $charUuid")
}
}
}
}
This was from the activity I displayed the characteristics in:
private val connectionEventListener by lazy {
ConnectionEventListener().apply {
onDisconnect = {
runOnUiThread {
alert {
title = "Disconnected"
message = "Disconnected from device."
positiveButton("OK") { onBackPressed() }
}.show()
}
}
onCharacteristicRead = { _, characteristic ->
Timber.i("Read from ${characteristic.uuid}: ${characteristic.value.toHexString()}")
}
onCharacteristicWrite = { _, characteristic ->
Timber.i("Wrote to: ${characteristic.uuid}")
}
onMtuChanged = { _, mtu ->
Timber.i("MTU updated to: $mtu")
}
onCharacteristicChanged = { _, characteristic ->
Timber.i("Value changed on ${characteristic.uuid}: ${characteristic.value.toHexString()}")
}
onNotificationsEnabled = { _, characteristic ->
Timber.i("Notify enabled on: ${characteristic.uuid}")
notifyingCharacteristics.add(characteristic.uuid)
}
onNotificationsDisabled = { _, characteristic ->
Timber.i("Notify disabled on: ${characteristic.uuid}")
notifyingCharacteristics.remove(characteristic.uuid)
}
}
}
I don't know what else I need to post here but I can edit if the need arises. The point here I am stuck and don't know how to proceed. If I am able to connect to the BLE device and display its characteristics and all that, how do I get it to send me data or send to it data? How do I go from here?
Related
I am trying to query nutrition history data from Google Fit on Android.
The data is inserted via the food-logging app (Lose It).
But when I query the data from the Google Fit History client it retrieves the data only for the first item I inserted in the app.
i.e. If I log breakfast with juice and bagel, the macros that will be retrieved from the client are only for the bagel.
My code:
val nowInMillis = 1625656053000;
val weekAgoInMillis = 1625137653000;
val fitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_NUTRITION)
.addDataType(DataType.AGGREGATE_NUTRITION_SUMMARY)
.build()
val account = GoogleSignIn.getAccountForExtension(activity, fitnessOptions)
coroutineScope.launch {
Fitness.getHistoryClient(activity, account)
.readDailyTotal(DataType.TYPE_NUTRITION)
.addOnSuccessListener { response ->
for (dp in response.dataPoints) {
Log.i(TAG, "Data point:")
Log.i(TAG, "\tType: ${dp.dataType.name}")
Log.i(
TAG, "\tStart: ${
Calendar.getInstance().apply {
timeInMillis = dp.getStartTime(TimeUnit.MILLISECONDS)
}.time
}")
Log.i(
TAG,
"\tEnd: ${
Calendar.getInstance().apply {
timeInMillis = dp.getEndTime(TimeUnit.MILLISECONDS)
}.time
}")
for (field in dp.dataType.fields) {
Log.i(TAG, "\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
}
}
}
.addOnFailureListener { e -> Log.d(TAG, "OnFailure()", e) }
}
The printed result:
2021-07-06 22:55:17.988 30237-30237/com.metaflow.flutterhealthfitexample I/FlutterHealthFitPlugin: Type: com.google.nutrition.summary
2021-07-06 22:55:17.992 30237-30237/com.metaflow.flutterhealthfitexample I/FlutterHealthFitPlugin: Start: Tue Jul 06 01:00:01 GMT+03:00 2021
2021-07-06 22:55:17.996 30237-30237/com.metaflow.flutterhealthfitexample I/FlutterHealthFitPlugin: End: Tue Jul 06 01:00:01 GMT+03:00 2021
2021-07-06 22:55:18.002 30237-30237/com.metaflow.flutterhealthfitexample I/FlutterHealthFitPlugin: Field: nutrients Value: {calories=111.6, carbs.total=25.792, cholesterol=0.0, dietary_fiber=0.496, fat.saturated=0.0496, fat.total=0.496, protein=1.736, sodium=2.48, sugar=20.832}
2021-07-06 22:55:18.002 30237-30237/com.metaflow.flutterhealthfitexample I/FlutterHealthFitPlugin: Field: meal_type Value: 1
And if I look into the food logging app, I can see the next result, which clearly shows that nutrition info retrieved matches only juice:
I thought that maybe there is an issue with my code, so I downloaded a FitBit app, that able to show nutrition info from Google Fit as well, and it shows the same result:
Any option to get nutrition info for a complete day?
I am trying to create a recyclerview that shows images like instagram tab. I have checked this Implement Asymmetrical Grid Layout Manager Like Instagram Search question but it does not seem to do the work. I tried using gridlayoutmanager but I can't make it work. How to recreate it with google's FlexboxLayoutManager or a custom layout manager? Hope you'll answer. Regards.
You can use SpannedGridLayoutManager from Nick Butcher as layout manager for your RecyclerView.
Here how it looks like in action, i used it for creating the sample layout below.
This works for me so well SpannedGridLayoutManager.
// Sample usage from your Activity/Fragment
private fun setupSpannedGridLayout() {
val manager = SpannedGridLayoutManager(
object : GridSpanLookup {
override fun getSpanInfo(position: Int): SpanInfo {
// Conditions for 2x2 items
return if (position % 6 == 0 || position % 6 == 4) {
SpanInfo(2, 2)
} else {
SpanInfo(1, 1)
}
}
},
3, // number of columns
1f // how big is default item
)
recyclerView.layoutManager = manager
adapter = GridAdapter(arrayListOf())
recyclerView.adapter = adapter
}
PS: i am using kotlin here :).
As per the above answer use SpannedGridLayoutManager.
To show grids similar to instagram use below code
var spannedGridLayoutManager = SpannedGridLayoutManager(
orientation = SpannedGridLayoutManager.Orientation.VERTICAL,
spans = 3
)
spannedGridLayoutManager.itemOrderIsStable = true
spannedGridLayoutManager.spanSizeLookup =
SpannedGridLayoutManager.SpanSizeLookup { position ->
var x = 0
if (position % 9 == 0) {
x = position / 9
}
when {
position == 1 || x % 2 == 1 || (position - 1) % 18 == 0 ->
SpanSize(2, 2)
else ->
SpanSize(1, 1)
}
}
recyclerView.layoutManager = spannedGridLayoutManager
recyclerview.adapter = searchGridAdapter
for the newest Instagram layout in 2022 you can try this:
layoutManager.spanSizeLookup = IGLayoutManager.SpanSizeLookup { position ->
// 0 1 2
// s s b
// 3 4 5 6 7 8 9 10 11
// s s s s s s s s b
// 12 13 14 15 16 17 18 19 20 21 22 23 24
// s s s s s s s s s s s s b
// 25 26 27 28 29 30 31 32 33
// s s s s s s s s b
// 34 35 36 37 38 39 40 41 42 43 44 45 46
// s s s s s s s s s s s s b
// 47 48 49 50 51 52 53 54 55
// s s s s s s s s b
// 56 57 58 59 60 61 62 63 64 65 66 67 68
// s s s s s s s s s s s s b
// 69 70 71 72 73 74 75 76 77
// s s s s s s s s b
// 78 79 80 81 82 83 84 85 86 87 88 89 90
// s s s s s s s s s s s s b
// 91 92 93 94 95 96 97 98 99
// s s s s s s s s b
// 100 101 102 103 104 105 106 107 108 109 110 111 112
// s s s s s s s s s s s s b
// 113 114 115 116 117 118 119 120 121
// s s s s s s s s b
if (position < 2) {
// ILog.debug("???", "$position is small")
bigItemFlag = 0
SpanSize(1, 1)
}
else if (position == 2) {
// ILog.debug("???", "$position is big")
bigItemFlag = 11
SpanSize(1, 2)
}
else if (position % 11 == 0 && position == bigItemFlag) {
bigItemFlag += 13
// ILog.debug("???", "$position is big, $position mod 11 is ${position % 11}")
SpanSize(1, 2)
}
else if (position % 11 == 2 && position == bigItemFlag) {
bigItemFlag += 9
// ILog.debug("???", "$position is big, $position mod 11 is ${position % 11}")
SpanSize(1, 2)
}
else {
// ILog.debug("???", "$position is small")
SpanSize(1, 1)
}
}
I am developing an android HCE Application.
This is my simple code.
public class HceHostApduService extends HostApduService {
#Override
public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
try {
Thread.sleep(500);
}
catch (Exception ex) {
ex.printStackTrace();
}
return new byte[] {(byte) 0x90, (byte) 0x00};
}
#Override
public void onDeactivated(int reason) {
Log.d(TAG, String.format("onDeactivated : %d", reason));
}
Test Case without SIM
18 : send RF : RF Off
19 : recv RF : RF Off OK
20 : send RF : RF On
21 : recv RF : RF On OK
22 : send RF : REQA
23 : recv RF : 000400
24 : send RF : ANTI-COLLISION
25 : recv RF : 0008FA07F0
26 : send RF : SELECT[08FA07F0]
27 : recv RF : SAK : 0020
28 : send RF : RATS[FF]
29 : recv RF : 000578807002
30 : send PCSC: Connect
31 : recv PCSC: 3B781800005350000010079000
32 : send RF : 0A0000a4040007A0000004520001
33 : recv RF : FA0001
34 : send RF : FA0001
35 : recv RF : FA0001
...
55 : recv RF : FA0001
56 : send RF : FA0001
57 : recv RF : FA0001
58 : send RF : FA0001
59 : recv RF : 0A009000
Test Case with SIM
60 : send RF : RF Off
61 : recv RF : RF Off OK
62 : send RF : RF On
63 : recv RF : RF On OK
64 : send RF : REQA
65 : recv RF : 000400
66 : send RF : ANTI-COLLISION
67 : recv RF : 000102663A
68 : send RF : SELECT[0102663A]
69 : recv RF : SAK : 0020
70 : send RF : RATS[FF]
71 : recv RF : 000B7877B402806453430430
72 : send PCSC: Connect
73 : recv PCSC: 3B781800005350000010079000
74 : send RF : 0A0000a4040007A0000004520001
75 : recv RF :
76 : recv RF fail : 0x02
If run without sim, wait 500ms.
If run with sim, don't wait and disconnected.
Why are these two cases different?
Please help this problem.
I get this following error when I'm trying to make a HTTP call with okhttp:
W/System.err: java.io.EOFException: source exhausted prematurely
W/System.err: at okio.InflaterSource.read(InflaterSource.java:83)
W/System.err: at okio.GzipSource.read(GzipSource.java:80)
W/System.err: at okio.Buffer.writeAll(Buffer.java:1135)
W/System.err: at okio.RealBufferedSource.readString(RealBufferedSource.java:199)
W/System.err: at okhttp3.ResponseBody.string(ResponseBody.java:176)
W/System.err: at com.ethanwang.andplay.OKHttpTaskTag.doInBackground(OKHttpTaskTag.java:41)
W/System.err: at com.ethanwang.andplay.OKHttpTaskTag.doInBackground(OKHttpTaskTag.java:20)
W/System.err: at android.os.AsyncTask$2.call(AsyncTask.java:295)
W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
W/System.err: at java.lang.Thread.run(Thread.java:818)
I was only able to find this related issue: https://github.com/square/okhttp/issues/2193 but I have made sure that Content-Length is the correct length of the response body. Here is my logged okhttp request and response:
Request:
I/System.out: INFO: Sending request http://test.essaybot.com/msg/tag_search on Connection{test.essaybot.com:80, proxy=DIRECT# hostAddress=test.essaybot.com/34.208.145.50:80 cipherSuite=none protocol=http/1.1}
I/System.out: Content-Type: application/x-www-form-urlencoded
I/System.out: Content-Length: 33
I/System.out: Host: test.essaybot.com
I/System.out: Connection: Keep-Alive
I/System.out: Accept-Encoding: gzip
I/System.out: User-Agent: okhttp/3.11.0
Response:
I/System.out: INFO: Received response for http://test.essaybot.com/msg/tag_search in 62.2ms
I/System.out: Cache-Control: no-cache
I/System.out: Content-Encoding: gzip
I/System.out: Content-Length: 139
I/System.out: Content-Type: application/json
I/System.out: Set-Cookie: PHP_SESSION=0Q4rZJplDjrUNB4ZbWAG; Path=/; Max-Age=2592000
I/System.out: Set-Cookie: VISITOR_ID=65xGr53M1xM0waK8; Path=/; Max-Age=31536000
I/System.out: Date: Wed, 29 Aug 2018 20:45:06 GMT
I/System.out: INFO: Received response body bytes:
I/System.out: [31, -117, 8, 0, 0, 0, 0, 0, 0, -1, 44, -51, 65, 10, -62, 64, 12, 5, -48, -85, 72, -42, 93, -72, -18, -50, -91, 103, 16, -111, -23, 52, -83, 31, 66, -90, 100, 50, -94, -120, 119, -105, 116, -70, 9, 47, -16, -109, -1, 37, 54, 123, 104, -95, -15, 60, -112, 113, 109, -30, 52, -34, -24, 50, -65, 88, -67, 25, -45, 16, 70, -34, 97, 30, 115, 49, -28, 20, -128, 9, -108, 107, 80, -33, 96, -1, -12, 12, 22, 100, 36, 57, 93, -43, 89, 4, 43, 107, -65, -34, -74, 61, 58, -49, -56, -114, -94, -31, 86, -35, -110, -32, -8, 54, -23, 20, 88, -95, 107, -81, 101, 115, -44, 99, -109, 92, -98, 69, 66, 21, -119, -18, -65, 63, 0, 0, 0, -1, -1]
I/System.out: With length: 139
The server works perfectly fine with our web and iOS ends. It also worked fine with android until about two months ago, when it suddenly stopped working with no network changes. What else could cause this error?
-------------------------------------- updated --------------------------------------
It seems like the data in buffer is readable since from the debugger above there is readable text ([size=185 text={"err_no":0,"result":["Adventure","Advice","Art","Africa","Airliā¦]) annotated after the buffer variable. Also, the data from the debugger is different from the logged data:
data = {byte[8192]#4418}
0 = 123
1 = 34
2 = 101
3 = 114
4 = 114
5 = 95
6 = 110
7 = 111
8 = 34
9 = 58
10 = 48
11 = 44
12 = 34
13 = 114
14 = 101
15 = 115
16 = 117
17 = 108
18 = 116
19 = 34
20 = 58
21 = 91
22 = 34
23 = 65
24 = 100
25 = 118
26 = 101
27 = 110
28 = 116
29 = 117
30 = 114
31 = 101
32 = 34
33 = 44
34 = 34
35 = 65
36 = 100
37 = 118
38 = 105
39 = 99
40 = 101
41 = 34
42 = 44
43 = 34
44 = 65
45 = 114
46 = 116
47 = 34
48 = 44
49 = 34
50 = 65
51 = 102
52 = 114
53 = 105
54 = 99
55 = 97
56 = 34
57 = 44
58 = 34
59 = 65
60 = 105
61 = 114
62 = 108
63 = 105
64 = 110
65 = 101
66 = 115
67 = 34
68 = 44
69 = 34
70 = 65
71 = 110
72 = 120
73 = 105
74 = 101
75 = 116
76 = 121
77 = 34
78 = 44
79 = 34
80 = 65
81 = 114
82 = 116
83 = 105
84 = 102
85 = 105
86 = 99
87 = 105
88 = 97
89 = 108
90 = 32
91 = 73
92 = 110
93 = 116
94 = 101
95 = 108
96 = 108
97 = 105
98 = 103
99 = 101
100 = 110
101 = 99
102 = 101
103 = 34
104 = 44
105 = 34
106 = 65
107 = 112
108 = 112
109 = 115
110 = 34
111 = 44
112 = 34
113 = 65
114 = 100
115 = 100
116 = 105
117 = 99
118 = 116
119 = 105
120 = 111
121 = 110
122 = 34
123 = 44
124 = 34
125 = 65
126 = 117
127 = 115
128 = 116
129 = 114
130 = 97
131 = 108
132 = 105
133 = 97
134 = 34
135 = 44
136 = 34
137 = 65
138 = 105
139 = 114
140 = 98
141 = 110
142 = 98
143 = 34
144 = 44
145 = 34
146 = 65
147 = 103
148 = 105
149 = 110
150 = 103
151 = 34
152 = 44
153 = 34
154 = 65
155 = 100
156 = 118
157 = 101
158 = 114
159 = 116
160 = 105
161 = 115
162 = 105
163 = 110
164 = 103
165 = 34
166 = 44
167 = 34
168 = 65
169 = 108
170 = 99
171 = 111
172 = 104
173 = 111
174 = 108
175 = 34
176 = 44
177 = 34
178 = 65
179 = 115
180 = 105
181 = 97
182 = 34
183 = 93
184 = 125
185 = 0
186 = 0
187 = 0
188 = 0
189 = 0
190 = 0
191 = 0
192 = 0
193 = 0
194 = 0
195 = 0
196 = 0
197 = 0
198 = 0
199 = 0
----------------------- update ------------------------
I used the debugger to trace the problem and it seems like the gzipped response was decompressed twice. As shown in the pictures attached, read in InflaterSource.java is called twice. The exception is thrown at the second time it's being decompressed.
The first time read is called
The second time read is called, the exception is thrown
The server data is corrupt. It's supposed to contain a gzip trailer to indicate the end of the stream and that's missing.
I had this error on a retrofit GET request to a random site,I added
#Headers("Accept-Encoding: identity")
to fix it.
identity
Indicates the identity function (i.e. no compression, nor modification). This value is always considered as acceptable, even if not present.
you can use this code for getting inputstream. I hopeful it work for you.
private static String getResponseString(Response response) {
try {
InputStream inputStream;
String contentEncodingHeader = response.header("Content-Encoding");
if (contentEncodingHeader != null && contentEncodingHeader.equals("gzip")) {
// Read the zipped contents.
inputStream = new GZIPInputStream(response.body().byteStream());
} else {
// Read the normal contents.
inputStream = response.body().byteStream();
}
// Create and return buffered reader.
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
// Read stream.
StringBuilder sb = new StringBuilder("");
String line;
while ((line = br.readLine()) != null) {
sb.append(line.trim());
}
// Close everything.
response.body().close();
inputStream.close();
br.close();
return sb.toString();
} catch (IOException e) {
return "";
}
}
My android app passes in an OpenGL texture2D to my OpenCL kernel, however the pixels values being read by my kernel are out of bounds (>255).
I create my OpenGL texture like this:
GLES20.glGenTextures ( 2, targetTex, 0 );
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, targetTex[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, image_width, image_height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
The texture is then rendered to by binding it with a FBO:
targetFramebuffer = IntBuffer.allocate(1);
GLES20.glGenFramebuffers(1, targetFramebuffer);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFramebuffer.get(0));
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, targetTex[0], 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
I create the cl memory object like so:
mem_images[0] = clCreateFromGLTexture2D(m_clContext, CL_MEM_READ_ONLY, GL_TEXTURE_2D, 0, in_tex, &err);
and this is my OpenCL kernel:
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;
kernel void transfer_data(__read_only image2d_t input_image, __global float* debug) {
int2 pos;
uint4 pixel;
for (pos.y = get_global_id(1); pos.y < HEIGHT; pos.y += get_global_size(1)) {
for (pos.x = get_global_id(0); pos.x < WIDTH; pos.x += get_global_size(0)) {
pixel = read_imageui(input_image, sampler, pos);
debug[(pos.x + pos.y*WIDTH)*NUM_CHANNELS + 0] = pixel.x;
debug[(pos.x + pos.y*WIDTH)*NUM_CHANNELS + 1] = pixel.y;
debug[(pos.x + pos.y*WIDTH)*NUM_CHANNELS + 2] = pixel.z;
}
}
}
This is how I am enqueing the kernel:
local2Dsize[0] = 4;
local2Dsize[1] = 4;
global2Dsize[0] = clamp(image_width, 0, max_work_items[0]);
global2Dsize[1] = clamp(image_height, 0, max_work_items[1]);
global2Dsize[0] = ceil((float)global2Dsize[0]/(float)local2Dsize[0])*local2Dsize[0];
global2Dsize[1] = ceil((float)global2Dsize[1]/(float)local2Dsize[1])*local2Dsize[1];
twoDlocal_sizes["transfer_data"] = local2Dsize;
twoDglobal_sizes["transfer_data"] = global2Dsize;
kernels["transfer_data"] = clCreateKernel(m_program, "transfer_data", &err);
err = clSetKernelArg(kernels["transfer_data"], 0, sizeof(cl_mem), &mem_images[0]);
err |= clSetKernelArg(kernels["transfer_data"], 1, sizeof(cl_mem), &mems["image"]);
err = clEnqueueAcquireGLObjects(m_queue, 1, &mem_images[0], 0, 0, 0);
CHECK_ERROR_OCL(err, "acquiring GL objects", return false);
err = clEnqueueNDRangeKernel(m_queue, kernels["transfer_data"], 2, NULL, twoDglobal_sizes["transfer_data"], twoDlocal_sizes["transfer_data"], 0, NULL, NULL);
err = clFinish(m_queue);
err = clEnqueueReleaseGLObjects(m_queue, 1, &mem_images[0], 0, 0, 0);
CHECK_ERROR_OCL(err, "releasing GL objects", return false);
Now back on host when I print out these pixel values (from the array debug), they are way out of bounds and I don't understand why that is the case.
If you need more insight:
The overall aim of my project is to obtain camera frames in form of an OpenGL texture, process them using OpenCL and render the output back to the screen.
However the texture obtained from android camera can only be bound to GL_TEXTURE_EXTERNAL_OES (http://developer.android.com/reference/android/graphics/SurfaceTexture.html), and this is not a valid texture to create an OpenCL memory object from.
Therefore I am rendering the camera output to a GL_TEXTURE_2D and passing that to OpenCL.
I am sure that the pixels are being rendered to the texture correctly, because when I display the texture on the screen (without any OpenCL involved) it displays the image properly.
I did some debugging by creating a texture (as opposed to getting data from the camera) and passing that to opencl. So these are the mappings I get:
0 -> 0
1 -> 7172
2 -> 8196
3 -> 8710
4 -> 9220
5 -> 9477
6 -> 9734
7 -> 9991
8 -> 10244
9 -> 10372
10 -> 10501
11 -> 10629
12 -> 10758
13 -> 10886
14 -> 11015
15 -> 11143
16 -> 11268
17 -> 11332
18 -> 11396
19 -> 11460
20 -> 11525
21 -> 11589
22 -> 11653
23 -> 11717
24 -> 11782
25 -> 11846
26 -> 11910
27 -> 11974
28 -> 12039
29 -> 12103
30 -> 12167
31 -> 12231
32 -> 12292
33 -> 12324
34 -> 12356
35 -> 12388
36 -> 12420
37 -> 12452
38 -> 12484
39 -> 12516
40 -> 12549
41 -> 12581
42 -> 12613
43 -> 12645
44 -> 12677
45 -> 12709
46 -> 12741
47 -> 12773
48 -> 12806
49 -> 12838
50 -> 12870
51 -> 12902
52 -> 12934
53 -> 12966
54 -> 12998
55 -> 13030
56 -> 13063
57 -> 13095
58 -> 13127
59 -> 13159
60 -> 13191
61 -> 13223
62 -> 13255
63 -> 13287
64 -> 13316
65 -> 13332
66 -> 13348
67 -> 13364
68 -> 13380
69 -> 13396
70 -> 13412
71 -> 13428
72 -> 13444
73 -> 13460
74 -> 13476
75 -> 13492
76 -> 13508
77 -> 13524
78 -> 13540
79 -> 13556
80 -> 13573
81 -> 13589
82 -> 13605
83 -> 13621
84 -> 13637
85 -> 13653
86 -> 13669
87 -> 13685
88 -> 13701
89 -> 13717
90 -> 13733
91 -> 13749
92 -> 13765
93 -> 13781
94 -> 13797
95 -> 13813
96 -> 13830
97 -> 13846
98 -> 13862
99 -> 13878
100 -> 13894
101 -> 13910
102 -> 13926
103 -> 13942
104 -> 13958
105 -> 13974
106 -> 13990
107 -> 14006
108 -> 14022
109 -> 14038
110 -> 14054
111 -> 14070
112 -> 14087
113 -> 14103
114 -> 14119
115 -> 14135
116 -> 14151
117 -> 14167
118 -> 14183
119 -> 14199
120 -> 14215
121 -> 14231
122 -> 14247
123 -> 14263
124 -> 14279
125 -> 14295
126 -> 14311
127 -> 14327
128 -> 14340
129 -> 14348
130 -> 14356
131 -> 14364
132 -> 14372
133 -> 14380
134 -> 14388
135 -> 14396
136 -> 14404
137 -> 14412
138 -> 14420
139 -> 14428
140 -> 14436
141 -> 14444
142 -> 14452
143 -> 14460
144 -> 14468
145 -> 14476
146 -> 14484
147 -> 14492
148 -> 14500
149 -> 14508
150 -> 14516
151 -> 14524
152 -> 14532
153 -> 14540
154 -> 14548
155 -> 14556
156 -> 14564
157 -> 14572
158 -> 14580
159 -> 14588
160 -> 14597
161 -> 14605
162 -> 14613
163 -> 14621
164 -> 14629
165 -> 14637
166 -> 14645
167 -> 14653
168 -> 14661
169 -> 14669
170 -> 14677
171 -> 14685
172 -> 14693
173 -> 14701
174 -> 14709
175 -> 14717
176 -> 14725
177 -> 14733
178 -> 14741
179 -> 14749
180 -> 14757
181 -> 14765
182 -> 14773
183 -> 14781
184 -> 14789
185 -> 14797
186 -> 14805
187 -> 14813
188 -> 14821
189 -> 14829
190 -> 14837
191 -> 14845
192 -> 14854
193 -> 14862
194 -> 14870
195 -> 14878
196 -> 14886
197 -> 14894
198 -> 14902
199 -> 14910
200 -> 14918
201 -> 14926
202 -> 14934
203 -> 14942
204 -> 14950
205 -> 14958
206 -> 14966
207 -> 14974
208 -> 14982
209 -> 14990
210 -> 14998
211 -> 15006
212 -> 15014
213 -> 15022
214 -> 15030
215 -> 15038
216 -> 15046
217 -> 15054
218 -> 15062
219 -> 15070
220 -> 15078
221 -> 15086
222 -> 15094
223 -> 15102
224 -> 15111
225 -> 15119
226 -> 15127
227 -> 15135
228 -> 15143
229 -> 15151
230 -> 15159
231 -> 15167
232 -> 15175
233 -> 15183
234 -> 15191
235 -> 15199
236 -> 15207
237 -> 15215
238 -> 15223
239 -> 15231
240 -> 15239
241 -> 15247
242 -> 15255
243 -> 15263
244 -> 15271
245 -> 15279
246 -> 15287
247 -> 15295
248 -> 15303
249 -> 15311
250 -> 15319
251 -> 15327
252 -> 15335
253 -> 15343
254 -> 15351
255 -> 15359
On the left is the colour value that I input in the OpenGL texture and on the left is the corresponding value I get when I read the values in OpenCL.
The GL_UNSIGNED_BYTE texture is being mapped to OpenCL as CL_UNORM_INT8. You need to use read_imagef to read from these images rather than read_imageui. The values you are seeing when you use read_imageui are the raw bits of the internal floating point format.
You are not acquiring and releasing the GL objects before accessing them. This causes the kernel not to read to data in the GL internal buffer but a local CL copy of it.
Proper code: (BTW you should check those "err" values for errors)
local2Dsize[0] = 4;
local2Dsize[1] = 4;
global2Dsize[0] = clamp(image_width, 0, max_work_items[0]);
global2Dsize[1] = clamp(image_height, 0, max_work_items[1]);
global2Dsize[0] = ceil((float)global2Dsize[0]/(float)local2Dsize[0])*local2Dsize[0];
global2Dsize[1] = ceil((float)global2Dsize[1]/(float)local2Dsize[1])*local2Dsize[1];
twoDlocal_sizes["transfer_data"] = local2Dsize;
twoDglobal_sizes["transfer_data"] = global2Dsize;
kernels["transfer_data"] = clCreateKernel(m_program, "transfer_data", &err);
err = clSetKernelArg(kernels["transfer_data"], 0, sizeof(cl_mem), &mem_images[0]);
err |= clSetKernelArg(kernels["transfer_data"], 1, sizeof(cl_mem), &mems["image"]);
err = clEnqueueAcquireGLObjects(m_queue, 1, mem_images, NULL, NULL, NULL);
err = clEnqueueNDRangeKernel(m_queue, kernels["transfer_data"], 2, NULL, twoDglobal_sizes["transfer_data"], twoDlocal_sizes["transfer_data"], 0, NULL, NULL);
err = clEnqueueReleaseGLObjects (m_queue, 1, mem_images, NULL, NULL, NULL);
err = clFinish(m_queue);
Okay so I think it's a bug in the OpenCL implementation.
With a bit of help from Wolfram Alpha, I created a function to reverse the above mappings and obtain values in the range of 0 and 255.
float GL_to_CL(uint val) {
if (val >= 14340) return round(0.1245790*val - 1658.44); //>=128
if (val >= 13316) return round(0.0622869*val - 765.408); //>=64
if (val >= 12292) return round(0.0311424*val - 350.800); //>=32
if (val >= 11268) return round(0.0155702*val - 159.443); //>=16
float v = (float) val;
return round(0.0000000000000125922*pow(v,4.f) - 0.00000000026729*pow(v,3.f) + 0.00000198135*pow(v,2.f) - 0.00496681*v - 0.0000808829);
}
So the GL_to_CL() is a combination of 4 linear functions and a Quartic function.
I tried creating just a single function using polynomial interpolation, however the degree of the polynomial was too large and therefore more computationally expensive to solve than the combination of the 5 functions proposed above.
Another alternate is to use a 15k sized array to achieve constant time, however that would require me uploading roughly 15k Bytes to GPU's global memory.
Considering I am using the kernels to do image processing, I would be slightly pushing it.
Also accessing from global memory in OpenCL is often more expensive than performing some simple calculations.