Merge remote-tracking branch 'origin/master' into dev

# Conflicts:
#	app/src/main/java/com/geeksville/mesh/service/MeshService.kt
#	app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt
1.2-legacy
geeksville 2020-03-11 18:17:20 -07:00
commit 48ea4f50fa
19 zmienionych plików z 260 dodań i 82 usunięć

17
.github/workflows/android.yml vendored 100644
Wyświetl plik

@ -0,0 +1,17 @@
name: Android CI
# from https://medium.com/@wkrzywiec/github-actions-for-android-first-approach-f616c24aa0f9
on: push
jobs:
test:
name: Test
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@master
- name: set up JDK 1.8
uses: actions/setup-java@master
with:
java-version: 1.8
- name: Unit tests
run: bash ./gradlew test --stacktrace

Wyświetl plik

@ -1,6 +1,7 @@
# High priority
Work items for soon alpha builds
* update play store listing for public beta
* run services in sim mode on emulator
* show offline nodes as greyed out
* show time since last contact on the node info card

Wyświetl plik

@ -16,8 +16,8 @@ android {
applicationId "com.geeksville.mesh"
minSdkVersion 22 // The oldest emulator image I have tried is 22 (though 21 probably works)
targetSdkVersion 29
versionCode 9
versionName "0.0.9"
versionCode 104
versionName "0.1.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -45,7 +45,7 @@ android {
composeOptions {
kotlinCompilerVersion "1.3.61-dev-withExperimentalGoogleExtensions-20200129"
kotlinCompilerExtensionVersion "0.1.0-dev05"
kotlinCompilerExtensionVersion "0.1.0-dev06"
}
}

Wyświetl plik

@ -13,6 +13,7 @@ import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
@ -29,6 +30,7 @@ import com.geeksville.mesh.ui.AppStatus
import com.geeksville.mesh.ui.MeshApp
import com.geeksville.mesh.ui.ScanState
import com.geeksville.mesh.ui.Screen
import com.geeksville.util.Exceptions
import com.geeksville.util.exceptionReporter
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
@ -239,6 +241,8 @@ class MainActivity : AppCompatActivity(), Logging,
override fun onDestroy() {
unregisterMeshReceiver()
UIState.meshService =
null // When our activity goes away make sure we don't keep a ptr around to the service
super.onDestroy()
}
@ -328,6 +332,18 @@ class MainActivity : AppCompatActivity(), Logging,
}
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
return try {
super.dispatchTouchEvent(ev)
} catch (ex: Throwable) {
Exceptions.report(
ex,
"dispatchTouchEvent"
) // hide this Compose error from the user but report to the mothership
false
}
}
private val meshServiceReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
@ -395,7 +411,8 @@ class MainActivity : AppCompatActivity(), Logging,
private fun bindMeshService() {
debug("Binding to mesh service!")
// we bind using the well known name, to make sure 3rd party apps could also
logAssert(UIState.meshService == null)
if (UIState.meshService != null)
Exceptions.reportError("meshService was supposed to be null, ignoring (but reporting a bug)")
MeshService.startService(this)?.let { intent ->
// ALSO bind so we can use the api

Wyświetl plik

@ -530,6 +530,12 @@ class MeshService : Service(), Logging {
else -> TODO()
}
GeeksvilleApplication.analytics.track(
"data_receive",
DataPair("num_bytes", bytes.size),
DataPair("type", data.typValue)
)
}
/// Update our DB of users based on someone sending out a User subpacket
@ -686,10 +692,20 @@ class MeshService : Service(), Logging {
try {
reinitFromRadio()
val radioModel = DataPair("radio_model", myNodeInfo?.model ?: "unknown")
GeeksvilleApplication.analytics.track(
"mesh_connect",
DataPair("num_nodes", numNodes),
DataPair("num_online", numOnlineNodes)
DataPair("num_online", numOnlineNodes),
radioModel
)
// Once someone connects to hardware start tracking the approximate number of nodes in their mesh
// this allows us to collect stats on what typical mesh size is and to tell difference between users who just
// downloaded the app, vs has connected it to some hardware.
GeeksvilleApplication.analytics.setUserInfo(
DataPair("num_nodes", numNodes),
radioModel
)
} catch (ex: RemoteException) {
// It seems that when the ESP32 goes offline it can briefly come back for a 100ms ish which
@ -838,6 +854,12 @@ class MeshService : Service(), Logging {
sendToRadio(ToRadio.newBuilder().apply {
this.packet = packet
})
GeeksvilleApplication.analytics.track(
"data_send",
DataPair("num_bytes", payloadIn.size),
DataPair("type", typ)
)
}
override fun getRadioConfig(): ByteArray = toRemoteExceptions {

Wyświetl plik

@ -221,7 +221,7 @@ class RadioInterfaceService : Service(), Logging {
// Handle an incoming packet from the radio, broadcasts it as an android intent
private fun handleFromRadio(p: ByteArray) {
if(logReceives) {
if (logReceives) {
receivedPacketsLog.write(p)
receivedPacketsLog.flush()
}
@ -279,6 +279,9 @@ class RadioInterfaceService : Service(), Logging {
fromNum = service.getCharacteristic(BTM_FROMNUM_CHARACTER)
// We must set this to true before broadcasting connectionChanged
isConnected = true
safe!!.setNotify(fromNum, true) {
debug("fromNum changed, so we are reading new messages")
doReadFromRadio()
@ -286,7 +289,6 @@ class RadioInterfaceService : Service(), Logging {
// Now tell clients they can (finally use the api)
broadcastConnectionChanged(true)
isConnected = true
// Immediately broadcast any queued packets sitting on the device
doReadFromRadio()
@ -349,7 +351,7 @@ class RadioInterfaceService : Service(), Logging {
info("Closing radio interface service")
if (logSends)
sentPacketsLog.close()
if(logReceives)
if (logReceives)
receivedPacketsLog.close()
safe?.close()
safe = null

Wyświetl plik

@ -0,0 +1,19 @@
package com.geeksville.mesh.ui
import androidx.compose.Composable
import androidx.compose.onCommit
import com.geeksville.android.GeeksvilleApplication
/**
* Track compose screen visibility
*/
@Composable
fun analyticsScreen(name: String) {
onCommit(AppStatus.currentScreen) {
GeeksvilleApplication.analytics.sendScreenView(name)
onDispose {
GeeksvilleApplication.analytics.endScreenView()
}
}
}

Wyświetl plik

@ -7,7 +7,6 @@ import androidx.ui.core.Text
import androidx.ui.foundation.shape.corner.RoundedCornerShape
import androidx.ui.graphics.Color
import androidx.ui.layout.*
import androidx.ui.material.Button
import androidx.ui.material.Divider
import androidx.ui.material.MaterialTheme
import androidx.ui.material.TextButton
@ -75,9 +74,9 @@ private fun DrawerButton(
Surface(
modifier = modifier + LayoutPadding(
left = 8.dp,
start = 8.dp,
top = 8.dp,
right = 8.dp,
end = 8.dp,
bottom = 0.dp
),
color = backgroundColor,

Wyświetl plik

@ -3,11 +3,13 @@ package com.geeksville.mesh.ui
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.le.*
import android.content.Context
import android.os.ParcelUuid
import androidx.compose.*
import androidx.compose.Composable
import androidx.compose.Model
import androidx.compose.frames.modelMapOf
import androidx.compose.onCommit
import androidx.ui.core.ContextAmbient
import androidx.ui.core.LayoutModifier
import androidx.ui.core.Text
import androidx.ui.layout.Column
import androidx.ui.layout.LayoutGravity
@ -45,7 +47,7 @@ object ScanState : Logging {
debug("stopping scan")
try {
scanner!!.stopScan(callback)
} catch(ex: Throwable) {
} catch (ex: Throwable) {
warn("Ignoring error stopping scan, probably BT adapter was disabled suddenly: ${ex.message}")
}
callback = null
@ -67,7 +69,9 @@ fun BTScanScreen() {
val bluetoothAdapter =
(context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter
onActive {
analyticsScreen(name = "settings")
onCommit(AppStatus.currentScreen) {
ScanState.debug("BTScan component active")
ScanUIState.selectedMacAddr = RadioInterfaceService.getBondedDeviceAddress(context)
val scanCallback = object : ScanCallback() {
@ -117,10 +121,10 @@ fun BTScanScreen() {
/// The following call might return null if the user doesn't have bluetooth access permissions
val s: BluetoothLeScanner? = bluetoothAdapter.bluetoothLeScanner
if(s == null) {
ScanUIState.errorText = "This application requires bluetooth access. Please grant access in android settings."
}
else {
if (s == null) {
ScanUIState.errorText =
"This application requires bluetooth access. Please grant access in android settings."
} else {
ScanState.debug("starting scan")
// filter and only accept devices that have a sw update service
@ -138,6 +142,7 @@ fun BTScanScreen() {
}
onDispose {
ScanState.debug("BTScan component deactivated")
ScanState.stopScan()
}
}
@ -147,7 +152,10 @@ fun BTScanScreen() {
Text("An unexpected error was encountered. Please file a bug on our github: ${ScanUIState.errorText}")
} else {
if (ScanUIState.devices.isEmpty()) {
Text(text = "Looking for Meshtastic devices... (zero found)", modifier = LayoutGravity.Center)
Text(
text = "Looking for Meshtastic devices... (zero found)",
modifier = LayoutGravity.Center
)
CircularProgressIndicator() // Show that we are searching still
} else {

Wyświetl plik

@ -3,20 +3,22 @@ package com.geeksville.mesh.ui
import android.content.Intent
import android.graphics.Bitmap
import androidx.compose.Composable
import androidx.ui.core.ContextAmbient
import androidx.ui.core.Text
import androidx.ui.foundation.DrawImage
import androidx.ui.graphics.Image
import androidx.ui.graphics.ImageConfig
import androidx.ui.graphics.NativeImage
import androidx.ui.core.*
import androidx.ui.foundation.Box
import androidx.ui.graphics.*
import androidx.ui.graphics.colorspace.ColorSpace
import androidx.ui.graphics.colorspace.ColorSpaces
import androidx.ui.graphics.painter.ImagePainter
import androidx.ui.layout.*
import androidx.ui.material.MaterialTheme
import androidx.ui.material.OutlinedButton
import androidx.ui.material.ripple.Ripple
import androidx.ui.tooling.preview.Preview
import androidx.ui.unit.Density
import androidx.ui.unit.PxSize
import androidx.ui.unit.dp
import androidx.ui.unit.toRect
import com.geeksville.analytics.DataPair
import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging
import com.geeksville.mesh.R
@ -70,8 +72,39 @@ class AndroidImage(val bitmap: Bitmap) : Image {
}
}
/// Stolen from the Compose SimpleImage, replace with their real Image component someday
// TODO(mount, malkov) : remove when RepaintBoundary is a modifier: b/149982905
// This is class and not val because if b/149985596
private object ClipModifier : DrawModifier {
override fun draw(density: Density, drawContent: () -> Unit, canvas: Canvas, size: PxSize) {
canvas.save()
canvas.clipRect(size.toRect())
drawContent()
canvas.restore()
}
}
/// Stolen from the Compose SimpleImage, replace with their real Image component someday
@Composable
fun ScaledImage(
image: Image,
modifier: Modifier = Modifier.None,
tint: Color? = null
) {
with(DensityAmbient.current) {
val imageModifier = ImagePainter(image).toModifier(
scaleFit = ScaleFit.FillMaxDimension,
colorFilter = tint?.let { ColorFilter(it, BlendMode.srcIn) }
)
Box(modifier + ClipModifier + imageModifier)
}
}
@Composable
fun ChannelContent(channel: Channel = Channel("Default", 7)) {
analyticsScreen(name = "channel")
val typography = MaterialTheme.typography()
val context = ContextAmbient.current
@ -87,14 +120,18 @@ fun ChannelContent(channel: Channel = Channel("Default", 7)) {
// val image = imageResource(id = R.drawable.qrcode)
val image = AndroidImage(UIState.getChannelQR(context))
Container(modifier = LayoutGravity.Center + LayoutSize.Min(200.dp, 200.dp)) {
DrawImage(image = image)
}
ScaledImage(
image = image,
modifier = LayoutGravity.Center + LayoutSize.Min(200.dp, 200.dp)
)
Ripple(bounded = false) {
OutlinedButton(modifier = LayoutGravity.Center + LayoutPadding(left = 24.dp),
OutlinedButton(modifier = LayoutGravity.Center + LayoutPadding(start = 24.dp),
onClick = {
GeeksvilleApplication.analytics.track("channel_share") // track how many times users share channels
GeeksvilleApplication.analytics.track(
"share",
DataPair("content_type", "channel")
) // track how many times users share channels
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND

Wyświetl plik

@ -2,7 +2,6 @@ package com.geeksville.mesh.ui
import androidx.compose.Composable
import androidx.compose.state
import androidx.ui.animation.Crossfade
import androidx.ui.core.ContextAmbient
import androidx.ui.core.Text
import androidx.ui.layout.Column
@ -34,6 +33,8 @@ fun getInitials(name: String): String {
@Composable
fun HomeContent() {
analyticsScreen(name = "users")
Column {
Row {
VectorImage(
@ -124,35 +125,30 @@ fun previewView() {
@Composable
private fun AppContent(openDrawer: () -> Unit) {
Crossfade(AppStatus.currentScreen) { screen ->
Surface(color = (MaterialTheme.colors()).background) {
// crossfade breaks onCommit behavior because it keeps old views around
//Crossfade(AppStatus.currentScreen) { screen ->
Surface(color = (MaterialTheme.colors()).background) {
Column {
TopAppBar(
title = { Text(text = "Meshtastic") },
navigationIcon = {
Container(LayoutSize(40.dp, 40.dp)) {
VectorImageButton(R.drawable.ic_launcher_new_foreground) {
openDrawer()
}
Column {
TopAppBar(
title = { Text(text = "Meshtastic") },
navigationIcon = {
Container(LayoutSize(40.dp, 40.dp)) {
VectorImageButton(R.drawable.ic_launcher_new_foreground) {
openDrawer()
}
}
)
// VerticalScroller breaks flexible layouts - because verticalscrollers have 'infinite' height
// VerticalScroller(modifier = LayoutFlexible(1f)) {
//if (screen != Screen.settings)
// ScanState.stopScan() // Nasty hack to teardown the bt scanner
when (screen) {
Screen.messages -> MessagesContent()
Screen.settings -> SettingsContent()
Screen.users -> HomeContent()
Screen.channel -> ChannelContent()
else -> TODO()
}
//}
)
when (AppStatus.currentScreen) {
Screen.messages -> MessagesContent()
Screen.settings -> SettingsContent()
Screen.users -> HomeContent()
Screen.channel -> ChannelContent()
else -> TODO()
}
}
}
//}
}

Wyświetl plik

@ -39,7 +39,7 @@ fun MessageCard(msg: TextMessage, modifier: Modifier = Modifier.None) {
Row(modifier = modifier) {
UserIcon(NodeDB.nodes[msg.from])
Column(modifier = LayoutPadding(left = 12.dp)) {
Column(modifier = LayoutPadding(start = 12.dp)) {
Row {
val nodes = NodeDB.nodes
@ -51,7 +51,7 @@ fun MessageCard(msg: TextMessage, modifier: Modifier = Modifier.None) {
ProvideEmphasis(emphasis = TimestampEmphasis) {
Text(
text = dateFormat.format(msg.date),
modifier = LayoutPadding(left = 8.dp),
modifier = LayoutPadding(start = 8.dp),
style = MaterialTheme.typography().caption
)
}
@ -67,6 +67,8 @@ fun MessageCard(msg: TextMessage, modifier: Modifier = Modifier.None) {
@Composable
fun MessagesContent() {
analyticsScreen(name = "messages")
Column(modifier = LayoutSize.Fill) {
val sidePad = 8.dp
@ -79,8 +81,8 @@ fun MessagesContent() {
messages.forEach { msg ->
MessageCard(
msg, modifier = LayoutPadding(
left = sidePad,
right = sidePad,
start = sidePad,
end = sidePad,
top = topPad,
bottom = topPad
)

Wyświetl plik

@ -69,7 +69,7 @@ fun NodeInfoCard(node: NodeInfo) {
// Text("Node: ${it.user?.longName}")
Row(modifier = LayoutPadding(16.dp)) {
UserIcon(
modifier = LayoutPadding(left = 0.dp, top = 0.dp, right = 0.dp, bottom = 0.dp),
modifier = LayoutPadding(start = 0.dp, top = 0.dp, end = 0.dp, bottom = 0.dp),
user = node
)

Wyświetl plik

@ -1,7 +1,6 @@
package com.geeksville.mesh.ui
import androidx.compose.Composable
import androidx.compose.ambient
import androidx.compose.state
import androidx.ui.core.ContextAmbient
import androidx.ui.core.Text

Wyświetl plik

@ -2,25 +2,23 @@ package com.geeksville.mesh.ui
import androidx.annotation.DrawableRes
import androidx.compose.Composable
import androidx.ui.core.DensityAmbient
import androidx.ui.core.Modifier
import androidx.ui.foundation.Clickable
import androidx.ui.foundation.Icon
import androidx.ui.graphics.Color
import androidx.ui.graphics.vector.DrawVector
import androidx.ui.layout.Container
import androidx.ui.layout.LayoutSize
import androidx.ui.material.ripple.Ripple
import androidx.ui.material.IconButton
import androidx.ui.res.vectorResource
import androidx.ui.unit.dp
@Composable
fun VectorImageButton(@DrawableRes id: Int, onClick: () -> Unit) {
Ripple(bounded = false) {
Clickable(onClick = onClick) {
VectorImage(id = id /* , modifier = LayoutSize(40.dp, 40.dp) */)
}
//Ripple(bounded = false) {
IconButton(onClick = onClick) {
Icon(vectorResource(id) /* , modifier = LayoutSize(40.dp, 40.dp) */)
}
//}
}
/* fun AppBarIcon(icon: Image, onClick: () -> Unit) {
@ -40,13 +38,13 @@ fun VectorImage(
) {
val vector = vectorResource(id)
// WithDensity {
Container(
modifier = modifier + LayoutSize(
vector.defaultWidth,
vector.defaultHeight
)
) {
DrawVector(vector, tint)
}
Container(
modifier = modifier + LayoutSize(
vector.defaultWidth,
vector.defaultHeight
)
) {
DrawVector(vector, tint)
}
// }
}

@ -1 +1 @@
Subproject commit e1a48b6e75c2ec3ad3995165a7b4fb64ce597e02
Subproject commit f309ee8f9e9db37daabd7c76da683e052ef62f7a

Wyświetl plik

@ -2,7 +2,7 @@
buildscript {
ext.kotlin_version = '1.3.61'
ext.compose_version = '0.1.0-dev05'
ext.compose_version = '0.1.0-dev06'
repositories {
google()
jcenter()
@ -18,7 +18,7 @@ buildscript {
// Check that you have the Google Services Gradle plugin v4.3.2 or later
// (if not, add it).
classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta01'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta02'
// protobuf plugin - docs here https://github.com/google/protobuf-gradle-plugin
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.11'

Wyświetl plik

@ -0,0 +1,61 @@
# Found a problem with our android app?
Oops sorry about that ;-). Android sends us automated crash reports for some types of failures but not all failures.
It would be super useful if you could help us by capturing a "logcat" file of the app while it was doing the bad thing and you attach that file to a github [issue](https://github.com/meshtastic/Meshtastic-Android/issues).
Here's how to do that...
Setup your phone & PC to allow debugging:
* Install "adb" (android debug bridge). [Here's](https://lifehacker.com/the-easiest-way-to-install-androids-adb-and-fastboot-to-1586992378)
a tutorial I found on the web. Please let me know if it works for you.
* [Enable](https://www.howtogeek.com/129728/how-to-access-the-developer-options-menu-and-enable-usb-debugging-on-android-4.2/)
developer mode on your phone. The procedure might be slightly different for some phones, if necessary google for "enable developer mode YOURPHONENAME"
* Connect your phone to your PC USB port. A dialog on the phone will say "do you want to allow this PC to access debug mode on your phone?"
Say yes and also click the checkbox to always allow your PC access.
* type "adb devices" at your computer shell prompt, you should see your phone listed. If you see that ADB is working fine.
* Long press on the meshtastic app and choose "force stop", to ensure that we are starting from scratch for this log (it will make it easier to understand it)
* If you have a Mac or Linux type:
```
adb shell 'logcat --pid=$(pidof -s com.geeksville.mesh)' | tee newlogfile.txt
```
* If you have a PC type:
```
adb shell "logcat --pid=$(pidof -s com.geeksville.mesh)" >newlogfile.txt (I don't know the equivalent of TEE for windows?)
```
This will capture a bunch of logging information as you use the app. Please go through the app to the part that was giving you troubles (No device listed on the settings screen etc). And then press
ctrl-c in the adb window to stop logging. Please open a github [issue](https://github.com/meshtastic/Meshtastic-Android/issues) describing the problem and attach the log file. We'll get back to you with what we find (possibly with some extra questions).
```
kevinh@kevin-server:~/development$ adb shell 'logcat --pid=$(pidof -s com.geeksville.mesh)' |
--------- beginning of main
03-07 17:10:05.669 13452 13452 W ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@fbf5fa0
03-07 17:10:05.927 13452 13452 D com.geeksville.mesh.MainActivity: Checking permissions
03-07 17:10:06.033 13452 13452 W geeksville.mes: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (greylist, reflection, allowed)
03-07 17:10:06.034 13452 13452 W geeksville.mes: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (greylist, reflection, allowed)
03-07 17:10:06.179 13452 13452 D com.geeksville.mesh.MainActivity: Binding to mesh service!
03-07 17:10:06.267 13452 13484 I geeksville.mes: Background concurrent copying GC freed 18374(1149KB) AllocSpace objects, 5(164KB) LOS objects, 49% free, 2384KB/4769KB, paused 329us total 124.902ms
03-07 17:10:06.267 13452 13484 W geeksville.mes: Reducing the number of considered missed Gc histogram windows from 1692 to 100
03-07 17:10:06.288 13452 24599 I FA : Tag Manager is not found and thus will not be used
03-07 17:10:06.500 13452 24593 I Adreno : QUALCOMM build : 4a00b69, I4e7e888065
03-07 17:10:06.500 13452 24593 I Adreno : Build Date : 04/09/19
03-07 17:10:06.500 13452 24593 I Adreno : OpenGL ES Shader Compiler Version: EV031.26.06.00
03-07 17:10:06.500 13452 24593 I Adreno : Local Branch : mybranche95a5ea3-cf05-f19a-a0e7-5cb90179c3d8
03-07 17:10:06.500 13452 24593 I Adreno : Remote Branch : quic/gfx-adreno.lnx.1.0
03-07 17:10:06.500 13452 24593 I Adreno : Remote Branch : NONE
03-07 17:10:06.500 13452 24593 I Adreno : Reconstruct Branch : NOTHING
03-07 17:10:06.500 13452 24593 I Adreno : Build Config : S P 8.0.6 AArch64
03-07 17:10:06.508 13452 24593 I Adreno : PFP: 0x016ee183, ME: 0x00000000
03-07 17:10:06.546 13452 24593 W Gralloc3: mapper 3.x is not supported
03-07 17:10:06.548 13452 24593 E libc : Access denied finding property "vendor.gralloc.disable_ahardware_buffer"
03-07 17:10:06.544 13452 13452 W RenderThread: type=1400 audit(0.0:7134): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=24620 scontext=u:r:untrusted_app:s0:c157,c257,c512,c768 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
03-07 17:10:06.607 13452 13452 I com.geeksville.mesh.service.MeshService: in isConnected=false
03-07 17:10:06.608 13452 13452 D com.geeksville.mesh.MainActivity: connchange false
03-07 17:10:06.608 13452 13452 D com.geeksville.mesh.MainActivity$mesh$1: connected to mesh service, isConnected=false
03-07 17:10:06.609 13452 13452 D com.geeksville.mesh.ui.AnalyticsLog: logging screen view messages
```

@ -1 +1 @@
Subproject commit b9616763f34cf5c09d8e0abe49fb79a5844ce27c
Subproject commit 188cf4fbb503ac0384f1fce4d3d3f0c2c9f07c02