major change: use service to run baresip

This commit is contained in:
Juha Heinanen
2018-06-21 19:31:45 +03:00
parent cc7f34bfdd
commit 4388c20ec9
9 changed files with 675 additions and 585 deletions

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tutpro.baresip"
android:installLocation="auto">
package="com.tutpro.baresip">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
@ -38,10 +37,10 @@
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:launchMode="singleTask" >
<intent-filter>
@ -108,6 +107,10 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="com.tutpro.baresip.MainActivity" />
</activity>
<service android:name=".BaresipService" >
</service>
</application>
</manifest>

View File

@ -0,0 +1,7 @@
package com.tutpro.baresip
object Api {
external fun call_peeruri(callp: String): String
}

View File

@ -0,0 +1,271 @@
package com.tutpro.baresip
import android.app.Notification
import android.app.Notification.VISIBILITY_PUBLIC
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.NetworkInfo
import android.os.IBinder
import android.support.annotation.Keep
import android.support.v4.app.NotificationCompat
import android.util.Log
import android.view.View
import android.widget.RemoteViews
import java.io.File
import java.io.FileInputStream
import java.io.ObjectInputStream
import android.support.v4.content.LocalBroadcastManager
class BaresipService: Service() {
private val LOG_TAG = "Baresip Service"
internal lateinit var intent: Intent
internal lateinit var nm: NotificationManager
internal lateinit var nb: NotificationCompat.Builder
internal lateinit var ni: Intent
internal lateinit var npi: PendingIntent
internal lateinit var nr: BroadcastReceiver
override fun onCreate() {
Log.d(LOG_TAG, "At onCreate")
intent = Intent("com.tutpro.baresip.EVENT")
intent.setPackage("com.tutpro.baresip")
nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
nb = NotificationCompat.Builder(this)
ni = Intent(this, MainActivity::class.java)
.setAction(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
npi = PendingIntent.getActivity(this, 0, ni, 0)
nr = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val intentExtras = intent.extras
val info = intentExtras.getParcelable<NetworkInfo>("networkInfo")
Log.d("Baresip", "Got event $info")
if (info.isConnected) {
if (disconnected) {
UserAgent.register(MainActivity.uas)
disconnected = false
}
} else {
disconnected = true
}
}
}
super.onCreate()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
when (intent.getAction()) {
"Start" -> {
Log.i(LOG_TAG, "Received Start Foreground Intent ");
val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav",
"error.wav", "message.wav", "notfound.wav", "ring.wav", "ringback.wav")
val path = applicationContext.filesDir.path
var file = File(path)
if (!file.exists()) {
Log.d(LOG_TAG, "Creating baresip directory")
try {
File(path).mkdirs()
} catch (e: Error) {
Log.e(LOG_TAG, "Failed to create directory: " + e.toString())
}
}
for (a in assets) {
file = File("$path/$a")
if (!file.exists()) {
Log.d(LOG_TAG, "Copying asset $a")
Utils.copyAssetToFile(applicationContext, a, "$path/$a")
} else {
Log.d(LOG_TAG, "Asset $a already copied")
}
}
file = File(path, "history")
if (file.exists()) {
try {
val fis = FileInputStream(file)
val ois = ObjectInputStream(fis)
@SuppressWarnings("unchecked")
MainActivity.history = ois.readObject() as ArrayList<History>
Log.d(LOG_TAG, "Restored History of ${MainActivity.history.size} entries")
ois.close()
fis.close()
} catch (e: Exception) {
Log.w(LOG_TAG, "InputStream exception: - " + e.toString())
}
}
ContactsActivity.generateContacts(path + "/contacts")
Thread(Runnable { baresipStart(path) }).start()
BaresipService.IS_SERVICE_RUNNING = true
registerReceiver(nr, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"))
showNotification()
}
"UpdateNotification" -> {
Log.i(LOG_TAG, "UpdateNotification")
updateNotification()
}
"Stop" -> {
Log.i(LOG_TAG, "Received Stop Foreground Intent")
baresipStop()
BaresipService.IS_SERVICE_RUNNING = false
unregisterReceiver(nr)
stopForeground(true)
stopSelf()
}
}
return START_STICKY;
}
private fun showNotification() {
nb.setVisibility(VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.ic_stat)
.setContentIntent(npi)
.setPriority(Notification.PRIORITY_MAX)
.setOngoing(true)
.setContent(RemoteViews(packageName, R.layout.notification))
startForeground(STATUS_NOTIFICATION_ID, nb.build());
}
@Keep
fun addUA(uap: String) {
Log.d(LOG_TAG, "addUA at BaresipService")
val ua = UserAgent(uap)
MainActivity.uas.add(ua)
if (UserAgent.ua_isregistered(uap)) {
Log.d(LOG_TAG, "Ua ${ua.account.aor} is registered")
MainActivity.images.add(R.drawable.dot_green)
} else {
Log.d(LOG_TAG, "Ua ${ua.account.aor} is NOT registered")
MainActivity.images.add(R.drawable.dot_yellow)
ua.register()
}
val intent = Intent("service event")
intent.putExtra("event", "ua added")
intent.putExtra("uap", uap)
intent.putExtra("callp", "")
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
updateNotification()
}
@Keep
fun updateStatus(event: String, uap: String, callp: String) {
Log.d(LOG_TAG, "updateStatus got event $event")
if (!IS_SERVICE_RUNNING) return
val ua = UserAgent.find(MainActivity.uas, uap)
if (ua == null) {
Log.e(LOG_TAG, "updateStatus did not find ua $uap")
return
}
val aor = ua.account.aor
for (account_index in MainActivity.uas.indices) {
if (MainActivity.uas[account_index].account.aor == aor) {
when (event) {
"registering", "unregistering" -> {
return
}
"registered" -> {
MainActivity.images[account_index] = R.drawable.dot_green
updateNotification()
if (!MainActivity.visible) return
}
"registering failed" -> {
MainActivity.images[account_index] = R.drawable.dot_red
updateNotification()
if (!MainActivity.visible) return
}
"call ringing" -> {
return
}
"call incoming" -> {
if (!Utils.isVisible()) {
Log.d(LOG_TAG, "Baresip is NOT visible")
val peer_uri = Api.call_peeruri(callp)
val huBuilder = NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat)
.setContentText("Incoming call from $peer_uri")
.setDefaults(Notification.DEFAULT_ALL)
.setPriority(Notification.PRIORITY_HIGH)
.setAutoCancel(true)
.setContentIntent(npi)
nm.notify(INCOMING_NOTIFICATION_ID, huBuilder.build())
}
}
"call established", "call closed" -> {
nb.mActions.clear()
nb.mContentText = ""
nm.notify(STATUS_NOTIFICATION_ID, nb.build())
}
}
}
}
val intent = Intent("service event")
intent.putExtra("event", event)
intent.putExtra("uap", uap)
intent.putExtra("callp", callp)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onDestroy() {
Log.i(LOG_TAG, "In onDestroy")
super.onDestroy();
}
fun updateNotification() {
val contentView = RemoteViews(getPackageName(), R.layout.notification)
for (i: Int in 0 .. 5) {
val resID = resources.getIdentifier("status$i", "id", packageName)
if (i < MainActivity.images.size) {
contentView.setImageViewResource(resID, MainActivity.images[i])
contentView.setViewVisibility(resID, View.VISIBLE)
} else {
contentView.setViewVisibility(resID, View.INVISIBLE)
}
}
if (MainActivity.images.size > 4)
contentView.setViewVisibility(R.id.etc, View.VISIBLE)
else
contentView.setViewVisibility(R.id.etc, View.INVISIBLE)
nb.setContent(contentView)
nm.notify(STATUS_NOTIFICATION_ID, nb.build())
}
external fun baresipStart(path: String)
external fun baresipStop()
companion object {
var IS_SERVICE_RUNNING = false
val STATUS_NOTIFICATION_ID = 101
val INCOMING_NOTIFICATION_ID = 102
var disconnected = false
}
init {
System.loadLibrary("baresip")
}
}

View File

@ -85,14 +85,14 @@ class ContactsActivity : AppCompatActivity() {
fun generateContacts(path: String) {
val content = Utils.getFileContents(File(path))
MainActivity.contacts_remove()
contacts_remove()
contacts.clear()
content.lines().forEach {
val parts = it.split("\"")
if (parts.size == 3) {
val name = parts[1]
val uri = parts[2].trim()
MainActivity.contact_add("\"$name\" $uri")
contact_add("\"$name\" $uri")
contacts.add(Contact(name, uri))
}
}
@ -121,6 +121,10 @@ class ContactsActivity : AppCompatActivity() {
if (c.name.equals(name, ignoreCase = true)) return true
return false
}
external fun contacts_remove()
external fun contact_add(contact: String)
}
}

View File

@ -10,6 +10,10 @@ import android.util.Log
import android.view.MenuItem
import android.widget.AdapterView
import android.widget.ListView
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.ObjectOutputStream
import java.text.SimpleDateFormat
import java.util.ArrayList
@ -147,4 +151,20 @@ class HistoryActivity : AppCompatActivity() {
now.get(Calendar.DAY_OF_MONTH) == time.get(Calendar.DAY_OF_MONTH)
}
companion object {
fun saveHistory() {
val file = File(MainActivity.filesPath, "history")
try {
val fos = FileOutputStream(file)
val oos = ObjectOutputStream(fos)
oos.writeObject(MainActivity.history)
oos.close()
fos.close()
} catch (e: IOException) {
Log.w("Baresip", "OutputStream exception: " + e.toString())
e.printStackTrace()
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,11 @@ import android.app.ActivityManager
import android.content.Context
import android.support.v7.app.AlertDialog
import android.util.Log
import android.net.ConnectivityManager
import java.io.*
import android.os.Build
import android.os.PowerManager
import android.app.KeyguardManager
import java.io.*
object Utils {
fun getFileContents(file: File): String {
@ -56,6 +54,25 @@ object Utils {
}
fun copyAssetToFile(context: Context, asset: String, path: String) {
try {
val `is` = context.assets.open(asset)
val os = FileOutputStream(path)
val buffer = ByteArray(512)
var byteRead: Int = `is`.read(buffer)
while (byteRead != -1) {
os.write(buffer, 0, byteRead)
byteRead = `is`.read(buffer)
}
os.close()
`is`.close()
} catch (e: IOException) {
Log.e("Baresip", "Failed to read asset " + asset + ": " +
e.toString())
}
}
fun alertView(context: Context, title: String, message: String) {
// val alertDialog = AlertDialog.Builder(context, android.R.style.Theme_Material_Dialog_Alert).create()
val alertDialog = AlertDialog.Builder(context).create()
@ -142,22 +159,10 @@ object Utils {
return res
}
fun isConnected(context: Context): Boolean {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = cm.activeNetworkInfo
if (activeNetwork != null && activeNetwork.isConnected) {
val networkType = activeNetwork.type
return networkType == ConnectivityManager.TYPE_WIFI || networkType == ConnectivityManager.TYPE_MOBILE
} else {
return false
}
}
fun foregrounded(): Boolean {
fun isVisible(): Boolean {
val appProcessInfo = ActivityManager.RunningAppProcessInfo()
ActivityManager.getMyMemoryState(appProcessInfo);
return ((appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) or
(appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE))
ActivityManager.getMyMemoryState(appProcessInfo)
return appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}
fun isDeviceLocked(context: Context): Boolean {

View File

@ -21,7 +21,6 @@
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:focusable="true"
android:focusableInTouchMode="true"
android:text="Display Name" >
@ -42,7 +41,6 @@
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Authentication Username" >
</TextView>
@ -60,7 +58,6 @@
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Authentication Password" >
</TextView>
@ -79,7 +76,6 @@
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Outbound Proxies" >
</TextView>
@ -107,7 +103,6 @@
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Registration Interval (sec)" >
</TextView>
@ -125,7 +120,6 @@
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Audio Codecs" >
</TextView>
@ -143,7 +137,6 @@
android:paddingTop="10dp"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Media Encryption" >
</TextView>

View File

@ -27,10 +27,8 @@
<string name="mediaEnc">Selects media transport encryption protocol.\n
ZRTP (recommended) means that ZRTP end-to-end media encryption negotiation is tried after
the call has been established.\n
DTLS-SRTP means that UDP/TLS/RTP/SAVP is offered in outgoing call and that RTP/SAVP,
RTP/SAVPF, or UDP/TLS/RTP/SAVP is used if offered in incoming call.\n
SRTP-MANF means that RTP/SAVPS is offered in outgoing call and that RTP/SAVP or RTP/SAVPF
is used if offered in incoming call.\n
DTLS-SRTPF means that UDP/TLS/RTP/SAVPF is offered in outgoing call and that RTP/SAVP,
RTP/SAVPF, UDP/TLS/RTP/SAVP, or UDP/TLS/RTP/SAVPF is used if offered in incoming call.\n
SRTP-MAND means that RTP/SAVP is offered in outgoing call and that RTP/SAVP or RTP/SAVPF
is used if offered in incoming call.\n
SRTP means that RTP/AVP is offered in outgoing call and that RTP/SAVP or RTP/SAVPF is used