From 1c3c07186eb344298130d8b09c33a659b3227403 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Tue, 5 Oct 2021 13:52:49 +0300 Subject: [PATCH] Added limited support for multiple simultaneously active network interfaces Show notification when baresip is started without network access --- app/src/main/AndroidManifest.xml | 1 + app/src/main/cpp/baresip.c | 78 ++--- app/src/main/kotlin/com/tutpro/baresip/Api.kt | 2 +- .../com/tutpro/baresip/BaresipService.kt | 269 ++++++++++++------ .../main/kotlin/com/tutpro/baresip/Config.kt | 1 - .../kotlin/com/tutpro/baresip/MainActivity.kt | 16 +- .../main/kotlin/com/tutpro/baresip/Utils.kt | 58 +++- app/src/main/res/values-fi/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 9 files changed, 271 insertions(+), 156 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 31742c10..9886f1ed 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + diff --git a/app/src/main/cpp/baresip.c b/app/src/main/cpp/baresip.c index bce0d737..30747fa1 100644 --- a/app/src/main/cpp/baresip.c +++ b/app/src/main/cpp/baresip.c @@ -5,19 +5,9 @@ #include #include #include +#include "logger.h" -#define LOGD(...) \ - if (log_level_get() < LEVEL_INFO) ((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip Lib", __VA_ARGS__)) - -#define LOGI(...) \ - if (log_level_get() < LEVEL_WARN) ((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip Lib", __VA_ARGS__)) - -#define LOGW(...) \ - if (log_level_get() < LEVEL_ERROR) ((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip Lib", __VA_ARGS__)) - -#define LOGE(...) \ - if (log_level_get() <= LEVEL_ERROR) ((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip Lib", __VA_ARGS__)) - +#define LOG_TAG "Baresip Lib" typedef struct baresip_context { JavaVM *javaVM; @@ -202,6 +192,8 @@ static void ua_event_handler(struct ua *ua, enum ua_event ev, len = re_snprintf(event_buf, sizeof event_buf, "call answered", prm); break; case UA_EVENT_CALL_LOCAL_SDP: + if (strcmp(prm, "offer") == 0) + return; len = re_snprintf(event_buf, sizeof event_buf, "call %sed", prm); break; case UA_EVENT_CALL_RINGING: @@ -436,23 +428,12 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { return JNI_VERSION_1_6; } -JNIEXPORT jint JNICALL -Java_com_tutpro_baresip_Api_net_1add_1address(JNIEnv *env, jobject thiz, jstring javaIp); - -JNIEXPORT jint JNICALL -Java_com_tutpro_baresip_Api_net_1use_1nameserver(JNIEnv *env, jobject thiz, jstring javaServers); - JNIEXPORT void JNICALL -Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instance, - jstring jPath, jstring jIpAddrs, jstring jNetInterface, jint jNetAf, jint jLogLevel) { +Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instance, jstring jPath, + jstring jAddrs, jint jLogLevel) { LOGI("starting baresip\n"); - const char *net_interface = (*env)->GetStringUTFChars(env, jNetInterface, 0); - const int net_af = jNetAf; - - const char *ip_addrs = (*env)->GetStringUTFChars(env, jIpAddrs, 0); - char start_error[64] = ""; JavaVM *javaVM = g_ctx.javaVM; @@ -468,6 +449,7 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc int err; const char *path = (*env)->GetStringUTFChars(env, jPath, 0); + const char *addrs = (*env)->GetStringUTFChars(env, jAddrs, 0); struct le *le; runLoggingThread(); @@ -487,15 +469,6 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc goto out; } - if (strlen(net_interface) > 0) { - struct config *theconf = conf_config(); - str_ncpy(theconf->net.ifname, net_interface, - sizeof(theconf->net.ifname)); - } - - if (net_af != AF_UNSPEC) - conf_config()->net.af = net_af; - err = baresip_init(conf_config()); if (err) { LOGW("baresip_init() failed (%d)\n", err); @@ -503,20 +476,21 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc goto out; } - if (strlen(ip_addrs) > 0) { - char* addr_list = (char*)malloc(strlen(ip_addrs)); + if (strlen(addrs) > 0) { + char* addr_list = (char*)malloc(strlen(addrs)); struct sa temp_sa; char buf[256]; net_flush_addresses(baresip_network()); - strcpy(addr_list, ip_addrs); + strcpy(addr_list, addrs); char *ptr = strtok(addr_list, ";"); while (ptr != NULL) { - LOGI("adding address '%s'", ptr); if (0 == sa_set_str(&temp_sa, ptr, 0)) { sa_ntop(&temp_sa, buf, 256); - net_add_address(baresip_network(), &temp_sa); + ptr = strtok(NULL, ";"); + net_add_address_ifname(baresip_network(), &temp_sa, ptr); } else { LOGE("invalid ip address %s\n", ptr); + ptr = strtok(NULL, ";"); res = EAFNOSUPPORT; } ptr = strtok(NULL, ";"); @@ -524,7 +498,7 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc free(addr_list); } - net_debug_log(); + // net_debug_log(); play_set_path(baresip_player(), path); @@ -572,7 +546,6 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc (*env)->DeleteLocalRef(env, javaUA); } - LOGI("allocating mqeue\n"); err = mqueue_alloc(&mq, mqueue_handler, NULL); if (err) { LOGW("mqueue_alloc failed (%d)\n", err); @@ -1674,26 +1647,25 @@ Java_com_tutpro_baresip_Api_net_1use_1nameserver(JNIEnv *env, jobject thiz, jstr } JNIEXPORT jint JNICALL -Java_com_tutpro_baresip_Api_net_1add_1address(JNIEnv *env, jobject thiz, jstring javaIp) { - const char *native_ip = (*env)->GetStringUTFChars(env, javaIp, 0); +Java_com_tutpro_baresip_Api_net_1add_1address_1ifname(JNIEnv *env, jobject thiz, jstring jAddr, + jstring jIfName) { + const char *addr = (*env)->GetStringUTFChars(env, jAddr, 0); + const char *name = (*env)->GetStringUTFChars(env, jIfName, 0); int res = 0; struct sa temp_sa; char buf[256]; - LOGI("adding address '%s'\n", native_ip); - if (str_len(native_ip) == 0) { - (*env)->ReleaseStringUTFChars(env, javaIp, native_ip); - return 0; - } - if (0 == sa_set_str(&temp_sa, native_ip, 0)) { + LOGD("adding address/ifname '%s/%s'\n", addr, name); + if (0 == sa_set_str(&temp_sa, addr, 0)) { sa_ntop(&temp_sa, buf, 256); re_thread_enter(); - net_add_address(baresip_network(), &temp_sa); + res = net_add_address_ifname(baresip_network(), &temp_sa, name); re_thread_leave(); } else { - LOGE("invalid ip address %s\n", native_ip); + LOGE("invalid ip address %s\n", addr); res = EAFNOSUPPORT; } - (*env)->ReleaseStringUTFChars(env, javaIp, native_ip); + (*env)->ReleaseStringUTFChars(env, jAddr, addr); + (*env)->ReleaseStringUTFChars(env, jIfName, name); return res; } @@ -1711,7 +1683,7 @@ Java_com_tutpro_baresip_Api_net_1rm_1address(JNIEnv *env, jobject thiz, jstring if (0 == sa_set_str(&temp_sa, native_ip, 0)) { sa_ntop(&temp_sa, buf, 256); re_thread_enter(); - net_rm_address(baresip_network(), &temp_sa); + res = net_rm_address(baresip_network(), &temp_sa); re_thread_leave(); } else { LOGE("invalid ip address %s\n", native_ip); diff --git a/app/src/main/kotlin/com/tutpro/baresip/Api.kt b/app/src/main/kotlin/com/tutpro/baresip/Api.kt index bd35057b..d40ac7cb 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Api.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Api.kt @@ -75,7 +75,7 @@ object Api { external fun log_level_set(level: Int) external fun net_use_nameserver(servers: String): Int - external fun net_add_address(ip_addr: String): Int + external fun net_add_address_ifname(ip_addr: String, if_name: String): Int external fun net_rm_address(ip_addr: String): Int external fun net_debug() external fun net_dns_debug() diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 16a06995..b56bcf04 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -19,6 +19,8 @@ import android.text.SpannableString import android.text.style.ForegroundColorSpan import android.view.View import android.widget.RemoteViews +import android.content.Intent +import android.content.BroadcastReceiver import androidx.annotation.ColorRes import androidx.annotation.Keep import androidx.annotation.StringRes @@ -48,15 +50,18 @@ class BaresipService: Service() { private lateinit var partialWakeLock: PowerManager.WakeLock private lateinit var proximityWakeLock: PowerManager.WakeLock private lateinit var wifiLock: WifiManager.WifiLock - private lateinit var br: BroadcastReceiver + private lateinit var bluetoothReceiver: BroadcastReceiver + private lateinit var hotSpotReceiver: BroadcastReceiver private var rtTimer: Timer? = null private var audioFocusRequest: AudioFocusRequest? = null private var audioFocusUsage = -1 private var origVolume = -1 private val btAdapter = BluetoothAdapter.getDefaultAdapter() + private var linkAddresses = mutableMapOf() private var activeNetwork: Network? = null - private var linkAddresses = mutableListOf() + private var hotSpotIsEnabled = false + private var hotSpotAddresses = mapOf() @SuppressLint("WakelockTimeout") override fun onCreate() { @@ -102,21 +107,35 @@ class BaresipService: Service() { override fun onAvailable(network: Network) { super.onAvailable(network) - Log.i(TAG, "Network $network is available") - updateNetwork() + Log.d(TAG, "Network $network is available") + // If API >= 26, this will be followed by onCapabilitiesChanged + if (isServiceRunning && VERSION.SDK_INT < 26) + updateNetwork() + } + + override fun onLosing(network: Network, maxMsToLive: Int) { + super.onLosing(network, maxMsToLive) + Log.d(TAG, "Network $network is losing after $maxMsToLive ms") } override fun onLost(network: Network) { super.onLost(network) - Log.i(TAG, "Network $network is lost") - if (activeNetwork == network) + Log.d(TAG, "Network $network is lost") + if (isServiceRunning) + updateNetwork() + } + + override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) { + super.onCapabilitiesChanged(network, caps) + Log.d(TAG, "Network $network capabilities changed: $caps") + if (isServiceRunning) updateNetwork() } override fun onLinkPropertiesChanged(network: Network, props: LinkProperties) { super.onLinkPropertiesChanged(network, props) - Log.i(TAG, "Network $network link properties changed") - if (activeNetwork == network) + Log.d(TAG, "Network $network link properties changed: $props") + if (isServiceRunning) updateNetwork() } @@ -124,6 +143,55 @@ class BaresipService: Service() { ) wm = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager + hotSpotIsEnabled = Utils.isHotSpotOn(wm) + + hotSpotReceiver = object : BroadcastReceiver() { + override fun onReceive(contxt: Context, intent: Intent) { + val action = intent.action + if ("android.net.wifi.WIFI_AP_STATE_CHANGED" == action) { + val state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0) + if (WifiManager.WIFI_STATE_ENABLED == state % 10) { + if (hotSpotIsEnabled) { + Log.d(TAG, "HotSpot is still enabled") + } else { + Log.d(TAG, "HotSpot is enabled") + hotSpotIsEnabled = true + Timer().schedule(1000) { + hotSpotAddresses = Utils.hotSpotAddresses() + Log.d(TAG, "HotSpot addresses $hotSpotAddresses") + if (hotSpotAddresses.isNotEmpty()) { + for ((k, v) in hotSpotAddresses) + if (Api.net_add_address_ifname(k, v) != 0) + Log.e(TAG, "Failed to add $v address $k") + Timer().schedule(2000) { + Api.uag_reset_transp(register = true, reinvite = false) + } + } else { + Log.w(TAG, "Could not get hotspot addresses") + } + } + } + } else { + if (!hotSpotIsEnabled) { + Log.d(TAG, "HotSpot is still disabled") + } else { + Log.d(TAG, "HotSpot is disabled") + hotSpotIsEnabled = false + if (hotSpotAddresses.isNotEmpty()) { + for ((k, _) in hotSpotAddresses) + if (Api.net_rm_address(k) != 0) + Log.e(TAG, "Failed to remove address $k") + hotSpotAddresses = mapOf() + Api.uag_reset_transp(register = true, reinvite = false) + } + } + } + } + } + } + + this.registerReceiver(hotSpotReceiver, + IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED")) tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager @@ -133,7 +201,7 @@ class BaresipService: Service() { wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Baresip") wifiLock.setReferenceCounted(false) - br = object : BroadcastReceiver() { + bluetoothReceiver = object : BroadcastReceiver() { override fun onReceive(ctx: Context, intent: Intent) { when (intent.action) { BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED -> { @@ -206,7 +274,7 @@ class BaresipService: Service() { filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED) filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED) - this.registerReceiver(br, filter) + this.registerReceiver(bluetoothReceiver, filter) } super.onCreate() @@ -230,26 +298,12 @@ class BaresipService: Service() { "Start" -> { - var ipAddrs = "" - for (n in cm.allNetworks) { - val props = cm.getLinkProperties(n) - if (props != null) { - for (a in props.linkAddresses) - linkAddresses.add(a) - val addrs = Utils.hostAddresses(props.linkAddresses) - if (addrs != "") { - if (ipAddrs == "") - ipAddrs = addrs - else - ipAddrs += ";$addrs" - } - } - } - updateDnsServers() - val assets = arrayOf("accounts", "config", "contacts", "busy.wav", "callwaiting.wav", - "error.wav", "ringback.wav") + val assets = arrayOf( + "accounts", "config", "contacts", "busy.wav", "callwaiting.wav", + "error.wav", "ringback.wav" + ) var file = File(filesPath) if (!file.exists()) { Log.d(TAG, "Creating baresip directory") @@ -278,13 +332,15 @@ class BaresipService: Service() { CallHistory.restore() Message.restore() - if (ipAddrs == "") - Log.w(TAG, "Starting baresip without IP addresses") + linkAddresses = linkAddresses() + var addrs = "" + for (la in linkAddresses) + addrs = "$addrs;${la.key};${la.value}" + + Log.d(TAG, "Link addresses: $addrs") Thread { - baresipStart( - filesPath, ipAddrs, "", Api.AF_UNSPEC, logLevel - ) + baresipStart(filesPath, addrs.removePrefix(";"), logLevel) }.start() isServiceRunning = true @@ -293,11 +349,22 @@ class BaresipService: Service() { if (AccountsActivity.noAccounts()) { val newIntent = Intent(this, MainActivity::class.java) - newIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or - Intent.FLAG_ACTIVITY_NEW_TASK + newIntent.flags = + Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or + Intent.FLAG_ACTIVITY_NEW_TASK newIntent.putExtra("action", "accounts") startActivity(newIntent) } + + if (linkAddresses.isEmpty()) { + val newIntent = Intent(this, MainActivity::class.java) + newIntent.flags = + Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or + Intent.FLAG_ACTIVITY_NEW_TASK + newIntent.putExtra("action", "no network") + startActivity(newIntent) + } + } "Call Reject" -> { @@ -333,8 +400,10 @@ class BaresipService: Service() { if (ua == null) Log.w(TAG, "onStartCommand did not find UA $uap") else - ChatsActivity.saveUaMessage(ua.account.aor, - intent.getStringExtra("time")!!.toLong()) + ChatsActivity.saveUaMessage( + ua.account.aor, + intent.getStringExtra("time")!!.toLong() + ) nm.cancel(MESSAGE_NOTIFICATION_ID) } @@ -344,8 +413,10 @@ class BaresipService: Service() { if (ua == null) Log.w(TAG, "onStartCommand did not find UA $uap") else - ChatsActivity.deleteUaMessage(ua.account.aor, - intent.getStringExtra("time")!!.toLong()) + ChatsActivity.deleteUaMessage( + ua.account.aor, + intent.getStringExtra("time")!!.toLong() + ) nm.cancel(MESSAGE_NOTIFICATION_ID) } @@ -376,7 +447,8 @@ class BaresipService: Service() { override fun onDestroy() { Log.d(TAG, "At Baresip Service onDestroy") super.onDestroy() - this.unregisterReceiver(br) + this.unregisterReceiver(bluetoothReceiver) + this.unregisterReceiver(hotSpotReceiver) if (am.isBluetoothScoOn) am.stopBluetoothSco() cleanService() if (isServiceRunning) { @@ -847,6 +919,7 @@ class BaresipService: Service() { @Keep fun started() { Log.d(TAG, "Received 'started' from baresip") + Api.net_debug() val intent = Intent("service event") intent.putExtra("event", "started") intent.putExtra("params", arrayListOf(callActionUri)) @@ -1084,56 +1157,87 @@ class BaresipService: Service() { } private fun updateNetwork() { + + if (!isServiceRunning) + return + /* for (n in cm.allNetworks) - Log.i(TAG, "NETWORK $n with caps ${cm.getNetworkCapabilities(n)} and props " + + Log.d(TAG, "NETWORK $n with caps ${cm.getNetworkCapabilities(n)} and props " + "${cm.getLinkProperties(n)} is active ${isNetworkActive(n)}") */ - activeNetwork = activeNetwork() + + updateDnsServers() + + val lnAddrs = linkAddresses() + + Log.d(TAG, "Old/new link addresses $linkAddresses/$lnAddrs") + + var added = 0 + for (a in lnAddrs) + if (!linkAddresses.containsKey(a.key)) { + if (Api.net_add_address_ifname(a.key, a.value) != 0) + Log.e(TAG, "Failed to add address: $a") + else + added++ + } + var removed = 0 + for (a in linkAddresses) + if (!lnAddrs.containsKey(a.key)) { + if (Api.net_rm_address(a.key) != 0) + Log.e(TAG, "Failed to remove address: $a") + else + removed++ + } + + val active = activeNetwork() + Log.d(TAG, "Added/Removed/Active = $added/$removed/$active") + + if (added > 0 || removed > 0 || active != activeNetwork) { + linkAddresses = lnAddrs + activeNetwork = active + Api.uag_reset_transp(register = true, reinvite = true) + } + + Api.net_debug() + if (activeNetwork != null) { - val linkCaps = cm.getNetworkCapabilities(activeNetwork) - val linkProps = cm.getLinkProperties(activeNetwork) - Log.d(TAG, "Using active network $activeNetwork with caps: $linkCaps, props: $linkProps") - val lnAddrs = mutableListOf() - for (n in cm.allNetworks) { - val props = cm.getLinkProperties(n) - if (props != null) - for (a in props.linkAddresses) - lnAddrs.add(a) - } - var addrUpdate = false - for (a in lnAddrs) - if (!linkAddresses.contains(a)) { - Api.net_add_address(a.address.hostAddress) - addrUpdate = true - } - for (a in linkAddresses) - if (!lnAddrs.contains(a)) { - Api.net_rm_address(a.address.hostAddress) - addrUpdate = true - } - val dnsUpdate = updateDnsServers() - if (addrUpdate) { - linkAddresses = lnAddrs - Api.uag_reset_transp(register = true, reinvite = true) - } else { - if (dnsUpdate) - UserAgent.register() - } - if (linkCaps != null && linkCaps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + val caps = cm.getNetworkCapabilities(activeNetwork) + if (caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { Log.d(TAG, "Acquiring WiFi Lock") wifiLock.acquire() - } else { - Log.d(TAG, "Releasing WiFi Lock") - wifiLock.release() + return } - } else - Log.w(TAG, "No active network") + } + Log.d(TAG, "Releasing WiFi Lock") + wifiLock.release() } - private fun updateDnsServers(): Boolean { + private fun linkAddresses(): MutableMap { + val lnAddrs = mutableMapOf() + for (n in cm.allNetworks) { + val caps = cm.getNetworkCapabilities(n) ?: continue + if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND) || + VERSION.SDK_INT < 28) { + val props = cm.getLinkProperties(n) ?: continue + for (la in props.linkAddresses) + if (la.scope == android.system.OsConstants.RT_SCOPE_UNIVERSE && + props.interfaceName != null) + lnAddrs[la.address.hostAddress] = props.interfaceName!! + } + } + if (hotSpotIsEnabled) { + hotSpotAddresses = Utils.hotSpotAddresses() + Log.d(TAG, "HotSpot addresses $hotSpotAddresses") + for ((k, v) in hotSpotAddresses) + lnAddrs[k] = v + } + return lnAddrs + } + + private fun updateDnsServers() { if (isServiceRunning && !dynDns) - return false + return val servers = mutableListOf() - // Use DNS servers first from active network (if given) + // Use DNS servers first from active network (if available) for (n in cm.allNetworks) if (isNetworkActive(n)) { val linkProps = cm.getLinkProperties(n) @@ -1157,10 +1261,8 @@ class BaresipService: Service() { } else { // Log.d(TAG, "Updated DNS servers: '${servers}'") dnsServers = servers - return true } } - return false } private fun activeNetwork(): Network? { @@ -1200,8 +1302,7 @@ class BaresipService: Service() { isServiceClean = true } - private external fun baresipStart(path: String, ipAddrs: String, netInterface: String, - netAf: Int, logLevel: Int) + private external fun baresipStart(path: String, addrs: String, logLevel: Int) private external fun baresipStop(force: Boolean) companion object { diff --git a/app/src/main/kotlin/com/tutpro/baresip/Config.kt b/app/src/main/kotlin/com/tutpro/baresip/Config.kt index 9099a69c..2b23492a 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Config.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Config.kt @@ -1,7 +1,6 @@ package com.tutpro.baresip import android.content.Context - import java.net.InetAddress import java.nio.charset.StandardCharsets diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index de4dc843..da1cf15d 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -21,6 +21,8 @@ import android.text.TextWatcher import android.view.* import android.view.inputmethod.InputMethodManager import android.widget.* +import android.content.Intent +import android.content.BroadcastReceiver import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi @@ -330,8 +332,12 @@ class MainActivity : AppCompatActivity() { } else { val latest = CallHistory.aorLatestHistory(aor) if (latest != null) - callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(latest.peerUri), - Utils.aorDomain(ua.account.aor))) + callUri.setText( + Utils.friendlyUri( + ContactsActivity.contactName(latest.peerUri), + Utils.aorDomain(ua.account.aor) + ) + ) } } } @@ -707,8 +713,14 @@ class MainActivity : AppCompatActivity() { "accounts" -> { resumeAction = "accounts" } + "no network" -> { + Utils.alertView(this, getString(R.string.notice), + getString(R.string.no_network)) + return + } "call" -> { if (Call.calls().isNotEmpty()) { + Toast.makeText(applicationContext, getString(R.string.call_already_active), Toast.LENGTH_SHORT).show() return diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt index fd12a540..5c7c1d27 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt @@ -10,6 +10,7 @@ import android.graphics.Bitmap import android.graphics.Bitmap.createScaledBitmap import android.graphics.Color import android.net.* +import android.net.wifi.WifiManager import android.os.Bundle import android.os.Environment import android.provider.DocumentsContract @@ -24,20 +25,21 @@ import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat - import java.io.* +import java.lang.reflect.Method import java.security.SecureRandom import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipFile import java.util.zip.ZipOutputStream - +import java.net.InetAddress +import java.net.NetworkInterface +import java.net.SocketException import javax.crypto.Cipher import javax.crypto.SecretKeyFactory import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.PBEKeySpec import javax.crypto.spec.SecretKeySpec - import kotlin.collections.ArrayList object Utils { @@ -106,10 +108,14 @@ object Utils { var u = uri if (uri.startsWith("<") && (uri.endsWith(">"))) u = uri.substring(1).substringBeforeLast(">") + u = u.replace(":5060", "") + u = u.replace(";transport=udp", "", true) + if (u.split(":").size == 3 || uriParams(u).isNotEmpty()) + return u return if (u.contains("@")) { val user = uriUserPart(u) val host = uriHostPart(u) - if (isE164Number(user) || (host == domain)) + if (isE164Number(user) || host == domain) user else "$user@$host" @@ -277,17 +283,6 @@ object Utils { return true } - fun hostAddresses(list: List?): String { - var result = "" - if (list != null) for (la in list) - if (la.scope == android.system.OsConstants.RT_SCOPE_UNIVERSE) - if (result == "") - result = la.address.hostAddress - else - result += ";" + la.address.hostAddress - return result - } - fun implode(list: List, sep: String): String { var res = "" for (s in list) { @@ -305,6 +300,39 @@ object Utils { return appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND } + fun isHotSpotOn(wm: WifiManager): Boolean { + try { + val method: Method = wm.javaClass.getDeclaredMethod("isWifiApEnabled") + method.isAccessible = true + return method.invoke(wm) as Boolean + } catch (ignored: Throwable) { + } + return false + } + + fun hotSpotAddresses(): Map { + val result = mutableMapOf() + try { + val interfaces: Enumeration = NetworkInterface.getNetworkInterfaces() + while (interfaces.hasMoreElements()) { + val iface: NetworkInterface = interfaces.nextElement() + val ifName = iface.name + if (ifName.startsWith("ap") || ifName.startsWith("wlan")) { + val addresses: Enumeration = iface.inetAddresses + while (addresses.hasMoreElements()) { + val inetAddress: InetAddress = addresses.nextElement() + if (!inetAddress.isLoopbackAddress && !inetAddress.isLinkLocalAddress) + result[inetAddress.hostAddress] = ifName + } + if (result.isNotEmpty()) return result + } + } + } catch (ex: SocketException) { + Log.e(TAG, ex.toString()) + } + return result + } + fun dtmfWatcher(callp: String): TextWatcher { return object : TextWatcher { override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {} diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 75fa7d41..19ff89b5 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -495,4 +495,5 @@ Sinulla ei ole yhtään tuettua video-kameraa. Näytä salasana + Ei verkkoyhteyttä! diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb958185..5cb60ad2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -448,4 +448,5 @@ Grant \"Camera\" permission to place or answer video calls. You don\'t have any supported video cameras. Show Password + No network connection!