/* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: * * The original file was part of Open Source IMSDROID * Copyright (C) 2010-2011, Mamadou Diop. * Copyright (C) 2011, Doubango Telecom. * * * Contact: Mamadou Diop * * This file is part of Open Source Doubango Framework. * * This is free software: you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.doubango.ngn; import android.app.ActivityManager; import android.app.Application; import android.app.KeyguardManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.hardware.SensorManager; import android.media.AudioManager; import android.net.ConnectivityManager; import android.os.Build; import android.os.PowerManager; import android.telephony.TelephonyManager; import android.util.Log; import android.view.Display; import android.view.WindowManager; import org.doubango.ngn.utils.NgnStringUtils; import org.doubango.utils.AndroidUtils; import org.doubango.utils.CpuFeatures_t; import org.doubango.utils.Utils; import java.lang.reflect.Field; import java.util.Arrays; /** @mainpage Foreword * *

Foreword

* android-ngn-stack is a NGN (Next Generation Network) stack for Android 2.x (or later) devices.
* The Stack is based on doubango framework. doubango is the world's most advanced open source * 3GPP IMS/RCS framework for both embedded and desktop systems.
* The main purpose is to provide an open source stack for the developers to build their own VoIP applications.
* This framework offers a unique set of features ranging from audio/video calls, content sharing, messaging, conferencing, enhanced address book to social presence. * All these features are implemented in accordance with the standards: GSMA RCS, 3GPP IMS or VoLTE.
* * @page Introduction * This document has been written by us (Doubango Telecom) to help developers to quickly create innovative multimedia applications * for the Android OS. If you are a developer and is looking for the best way to develop a NGN (VoIP, Messaging, Video Conferencing, ...) or rich application for Android * then your are at the right place.
* If you want to get help or have some feedbacks then please visit our website: http://code.google.com/p/imsdroid/ * *

Doubango Solution

* android-ngn-stack is part of Doubango Solution which include many components such as: * *

Client-side components

* - Boghe: IMS/RCS Client for Windows * - IMSDroid: IMS/RCS Client for Android using android-ngn-stack * - iDoubs: IMS/RCS Client for iOS (iPhone, iPad and iPod Touch) * *

Server-side components

* - OpenVCS: OpenVCS stands for Open Source Video Conferencing Server and is used to manage Multipoint Control Units (MCU). Each MCU (a.k.a Bridge) can handle up to 64 participants * - Flash2IMS: Adobe Flash to SIP/IMS Gateway. * *

Highlights

* * - SIP(RFC 3261, 3GPP TS 24.229 Rel-9) * - TCP and UDP over IPv4 or IPv6 * - Signalling Compression, SigComp(RFC 3320, 3485, 4077, 4464, 4465, 4896, 5049, 5112 and 1951) * * - Enhanced Address Book (XCAP storage, authorizations, presence, ...) * - GSMA Rich Communication Suite release 3 * - Partial supports for One Voice Profile V1.0.0 (GSMA VoLTE) * - Partial supports for MMTel UNI (used by GSMA RCS and GSMA VoLTE) * * - IMS-AKA registration (both AKA-v1 and AKA-v2), Digest MD5, Basic * - 3GPP Early IMS Security (3GPP TS 33.978) * - Proxy-CSCF discovery using DNS NAPTR+SRV * - Private extension headers for 3GPP * - Service Route discovery * - Subscription to reg event package (Honoring network initiated (re/de/un)-registration events) * * - 3GPP SMS Over IP (3GPP TS 23.038, 24.040, 24.011, 24.341 and 24.451) * - Voice Call (G729AB1, AMR-NB, iLBC, GSM, PCMA, PCMU, Speex-NB) * - Video Call (H264, MP4V-ES, Theora, H.263, H.263-1998, H.261) * - DTMF (RFC 4733) * - QoS negotiation using Preconditions (RFC 3312, 4032 and 5027) * - SIP Session Timers (RFC 4028) * - Provisional Response Acknowledgments (PRACK) * - Communication Hold (3GPP TS 24.610) * - Message Waiting Indication (3GPP TS 24.606) * - Calling E.164 numbers by using ENUM protocol (RFC 3761) * - NAT Traversal using STUN2 (RFC 5389) with possibilities to automatically discover the server by using DNS SRV (TURN already implemented and ICE is under tests) * * - One2One and Group Chat * - File Transfer and Content sharing * * * @page page__Setting_Up_NGN_project Setting up NGN project * @anchor anchor_Setting_Up_NGN_project * This section explain how to setup a NGN project using Eclipse.
* *

Checking out the source code

* To check out the source code of the NGN library you will need a SVN client.
* Use this command to anonymously check out the last project source: * @code * svn checkout http://imsdroid.googlecode.com/svn imsdroid * @endcode * The source code of the library is under: * @code * This/branches/2.0/android-ngn-stack * @endcode * *

Importing the NGN project into Eclipse

* The NGN project is the Next Generation Network library. * - Open eclipse * - Go to File -> Import -> General -> Existing Project into workspace * - Select android-ngn-stack folder and click Finish * * @image html ngn_eclipse_import.png "Importing project into your workspace" * *

Creating you first NGN application using Eclipse

* - Open Eclipse and select File -> New -> Android Project * - From the next window ("New Android Project") fill the text fields like this:
* - Project name: myFirstApp
* - Location: < set any path >
* - Build Target: Android 2.0 (at least)
* - Application name: myFirstApp
* - Package name: org.doubango.test
* - Check "Create Activity" and name it "Main"
* * @image html ngn_eclipse_newproj.png "Create your first NGN application" * - Click on Finish to create the project * - From the Eclipse package explorer, right click on myFirstApp and select "Properties" then "Android" from the left

* @image html ngn_eclipse_properties_1.png "NGN application properties" * - From the properties window, select "Add" button then select android-ngn-stack from the list of the available libraries

* @image html ngn_eclipse_properties_2.png "Add dependencies" * - Select "Java Compiler" from the left and change the version from 1.5 to 1.6

* @image html ngn_eclipse_jdk_version.png "Java Compiler version" * - Select "Java Build Path" from the left, then "Libraries"

* @image html ngn_eclipse_java_buil_path_1.png "Java Build Path 1/2" * - From "Java Build Path 1/2", select "Add JARs..." then android-ngn-stack/libs/simple-xml-2.3.4.jar, then "OK" to close the window

* @image html ngn_eclipse_java_buil_path_2.png "Java Build Path 2/2" * - Click on "OK" to close the window * *

Setting up Android Permissions

* In order to use the framework you must enable some user-permission in your Android manifest.
* Open myFirstApp/AndroidManifest.xml, then add this: * @code * * * * * * * * * * * * * * * * * * * * * * * @endcode * ... just before @code @endcode * * *

Declaring your app as NGN

* Decalaring your app as NGN is recommended if your are programming at high level.
* - From the Eclipse package explorer, open AndroidManifest.xml and select Application tab from below * - Click on browse (on the right of Name) then, select "NgnApplication" from the list

* @image html ngn_eclipse_declaring_ngn_app.png "Declaring your app as NGN" * * * @page Architecture * The stack offers three levels of programming: Low, Medium and High.
* Before building and running your project, you should take a look at the section @ref anchor_Setting_Up_NGN_project "explaining how to setup a NGN project". *

Low level

* This level allow you to directly have access to doubango functions through JNI. * This level is the most flexible one but is out of scoop because it's too difficult to manage.
* All functions used in this level are in one single package: org.doubango.tinyWRAP
* For example, the code below shows how to register to a SIP/IMS server: * @code * final String realm = "sip:doubango.org"; * final String privateIdentity = "001"; * final String publicIdentity = "sip:001@doubango.org"; * final String password = "my secret"; * final String proxyHost = "192.168.0.1"; * RegistrationSession registrationSession; * // Sip Callback * final SipCallback callback = new SipCallback(){ * @Override * public int OnDialogEvent(DialogEvent e) { * final SipSession sipSession = e.getBaseSession(); * final long sipSessionId = sipSession.getId(); * final short code = e.getCode(); * switch (code){ * case tinyWRAPConstants.tsip_event_code_dialog_connecting: * if(registrationSession != null && registrationSession.getId() == sipSessionId){ * // Registration in progress * } * break; * case tinyWRAPConstants.tsip_event_code_dialog_connected: * if(registrationSession != null && registrationSession.getId() == sipSessionId){ * // You are registered * } * break; * case tinyWRAPConstants.tsip_event_code_dialog_terminating: * if(registrationSession != null && registrationSession.getId() == sipSessionId){ * // You are unregistering * } * break; * case tinyWRAPConstants.tsip_event_code_dialog_terminated: * if(registrationSession != null && registrationSession.getId() == sipSessionId){ * // You are unregistered * } * break; * } * * return 0; * } * * @Override * public int OnRegistrationEvent(RegistrationEvent e) { * // low level events * return 0; * } * }; * // Create the SipStack * SipStack sipStack = new SipStack(callback, realm, privateIdentity, publicIdentity); * // Set Proxy Host and port * sipStack.setProxyCSCF(proxyHost, 5060, "UDP", "IPv4"); * // Set password * sipStack.setPassword(password); * if(sipStack.isValid()){ * if(sipStack.start()){ * registrationSession = new RegistrationSession(sipStack); * registrationSession.setFromUri(publicIdentity); * // Send SIP register request * registrationSession.register_(); * } * } * @endcode * *

Medium level

* This level is built on of the low level. The main advantage of this level is that it's flexible without * being too complicated as all low level functions are wrapped into comprehensive API. * For example, if you want to implement a multi-stack (multi-account) application this is the right level. * *

High level

* This level is built in top of the low level and is much easier than the later. * The High level is composed of a set of Services managed by a single NGN engine instance. Each service is responsible for * a particular task. For example, you have one service for SIP, one for contact management, one for networking etc etc
* *

NGN Engine

* The engine is a black box containing all the services. You must always retrieve the services through the engine.
* You must also start/stop the services through the NGN engine.
* The code below shows how to get an instance of the engine: * @code * // Gets an instance of the engine. This function will always returns the same instance * // which means that you can call it as many as you want from anywhere in your code * final NgnEngine mEngine = NgnEngine.getInstance(); * @endcode * The code below shows how to get some services from the engine: * @code * // Gets the configuration service * INgnConfigurationService mConfigurationService = mEngine.getConfigurationService(); * // Gets the SIP/IMS service * INgnSipService mSipService = mEngine.getSipService(); * // etc etc * @endocde * The code below shows how to start/stop the engine. * @code * // Starts the engine * mEngine.start(); * // Stops the engine * mEngine.stop(); * @endcode * Starting/Stopping the engine will start/stop all underlying services. * * /** * Global object defining the application. You should extends this class in your own * Android application. */ public class NgnApplication extends Application{ private final static String TAG = Utils.getTAG(NgnApplication.class.getCanonicalName()); private static NgnApplication sInstance; private static PackageManager sPackageManager; private static String sPackageName; private static String sDeviceURN; private static String sDeviceIMEI; private static int sSdkVersion; private static int sVersionCode; private static AudioManager sAudioManager; private static SensorManager sSensorManager; private static KeyguardManager sKeyguardManager; private static ConnectivityManager sConnectivityManager; private static PowerManager sPowerManager; private static PowerManager.WakeLock sPowerManagerLock; private static int sGlEsVersion; static final String sBuildModel = Build.MODEL.toLowerCase(); static final String[] sSLEs2FriendlyBuildModels = { "galaxy nexus", /* 4.1 */ "gt-i9100", /* Galaxy SII, 4.0.4 */ /*"gt-s5360",*//* 2.3.6 :robotic*/ "gt-s5570i", /* 2.3.0 */ "xt890", /* Motorola Razer i 4.0.4 */ "lg-p970" /* 2.3.4 */ }; static final String[] sSLEs2UnFriendlyBuildModels = { "gt-s5360",/* 2.3.6 :robotic*/ }; static final String[] sSetModeFriendlyBuildModels = { "gt-s5570i" /* 2.3.6 */ }; // This function is called by the package manager, you must never explicitly invoke it. // Do not forget to add/change the section in your manifest as done in IMSDroid or all other test apps public NgnApplication() { sInstance = this; } public static NgnApplication getInstance(){ return sInstance; } /** * Retrieve application's context * @return Android context */ public static Context getContext() { return getInstance(); } @Override public void onCreate() { super.onCreate(); sPackageManager = sInstance.getPackageManager(); sPackageName = sInstance.getPackageName(); Log.d(TAG,"Build.MODEL="+sBuildModel); Log.d(TAG,"Build.VERSION.SDK="+Build.VERSION.SDK); } /** * Gets Android SDK version * @return Android SDK version used to build the project */ public static int getSDKVersion(){ if(sSdkVersion == 0){ sSdkVersion = Integer.parseInt(Build.VERSION.SDK); } return sSdkVersion; } /** * Whether we need special hack for buggy speaker. For example, all Samsung devices * need to be hacked. * @return true if we need to apply the hack and false otherwise */ public static boolean useSetModeToHackSpeaker(){ return (isSamsung() && !isSamsungGalaxyMini() && getSDKVersion()<= 7) || sBuildModel.equalsIgnoreCase("blade") || // ZTE Blade sBuildModel.equalsIgnoreCase("htc_supersonic") || //HTC EVO sBuildModel.equalsIgnoreCase("U8110") || // Huawei U8110 sBuildModel.equalsIgnoreCase("U8150") // Huawei U8110 ; } public static boolean isARMv7WithoutNeon(){ return (isCpuARMv7() && !isCpuNeon()); } public static boolean isCpuARMv8(){ return ((AndroidUtils.getCpuFeatures().intValue() & CpuFeatures_t.ARMv7.swigValue()) != 0); } public static boolean isCpuARMv7(){ return ((AndroidUtils.getCpuFeatures().intValue() & CpuFeatures_t.ARMv7.swigValue()) != 0); } public static int androidCpuFeature(){ return AndroidUtils.getCpuFeatures().intValue(); } public static boolean isCpuNeon(){ return ((AndroidUtils.getCpuFeatures().intValue() & CpuFeatures_t.NEON.swigValue()) != 0); } /** * Whether the stack is running on a Samsung Galaxy Mini device * @return true if the stack is running on a Samsung Galaxy Mini device and false otherwise */ public static boolean isSamsungGalaxyMini(){ return sBuildModel.equalsIgnoreCase("gt-i5800"); } /** * Whether the stack is running on a Samsung Galaxy Mini device * @return true if the stack is running on a Samsung Galaxy Mini device and false otherwise */ public static boolean isSamsung(){ return sBuildModel.startsWith("gt-") || sBuildModel.contains("samsung") || sBuildModel.startsWith("sgh-") || sBuildModel.startsWith("sph-") || sBuildModel.startsWith("sch-"); } /** * Whether the stack is running on a HTC device * @return true if the stack is running on a HTC device and false otherwise */ public static boolean isHTC(){ return sBuildModel.startsWith("htc"); } /** * Whether the stack is running on a ZTE device * @return true if the stack is running on a ZTE device and false otherwise */ public static boolean isZTE(){ return sBuildModel.startsWith("zte"); } /** * Whether the stack is running on a LG device * @return true if the stack is running on a LG device and false otherwise */ public static boolean isLG(){ return sBuildModel.startsWith("lg-"); } /** * Whether the stack is running on a Toshiba device * @return true if the stack is running on a Toshiba device and false otherwise */ public static boolean isToshiba(){ return sBuildModel.startsWith("toshiba"); } /** * Whether the stack is running on a Hovis box * @return true if the stack is running on a Hovis box and false otherwise */ public static boolean isHovis(){ return sBuildModel.startsWith("hovis_box_"); } public static boolean isAudioRecreateRequired(){ return false; } public static boolean isSetModeAllowed(){ return isZTE() || isLG() || Arrays.asList(sSetModeFriendlyBuildModels).contains(sBuildModel); } public static boolean isBuggyProximitySensor(){ return isZTE(); } public static boolean isAGCSupported(){ return isSamsung() || isHTC(); } public static int getVersionCode(){ if(sVersionCode == 0 && sPackageManager != null){ try { sVersionCode = sPackageManager.getPackageInfo(sPackageName, 0).versionCode; } catch (NameNotFoundException e) { e.printStackTrace(); } } return sVersionCode; } public static String getVersionName(){ if(sPackageManager != null){ try { return sPackageManager.getPackageInfo(sPackageName, 0).versionName; } catch (NameNotFoundException e) { e.printStackTrace(); } } return "0.0"; } public static String getDeviceURN(){ if(NgnStringUtils.isNullOrEmpty(sDeviceURN)){ try{ final TelephonyManager telephonyMgr = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); final String msisdn = telephonyMgr.getLine1Number(); if(NgnStringUtils.isNullOrEmpty(msisdn)){ sDeviceURN = String.format("urn:imei:%s", telephonyMgr.getDeviceId()); } else{ sDeviceURN = String.format("urn:tel:%s", msisdn); } } catch(Exception e){ Log.d(TAG, e.toString()); sDeviceURN = "urn:uuid:3ca50bcb-7a67-44f1-afd0-994a55f930f4"; } } return sDeviceURN; } public static String getDeviceIMEI(){ if(NgnStringUtils.isNullOrEmpty(sDeviceIMEI)){ final TelephonyManager telephonyMgr = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); sDeviceIMEI = telephonyMgr.getDeviceId(); } return sDeviceIMEI; } public static AudioManager getAudioManager(){ if(sAudioManager == null){ sAudioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE); } return sAudioManager; } public static SensorManager getSensorManager(){ if(sSensorManager == null){ sSensorManager = (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE); } return sSensorManager; } public static KeyguardManager getKeyguardManager(){ if(sKeyguardManager == null){ sKeyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE); } return sKeyguardManager; } public static ConnectivityManager getConnectivityManager(){ if(sConnectivityManager == null){ sConnectivityManager = (ConnectivityManager) getContext().getSystemService(CONNECTIVITY_SERVICE); } return sConnectivityManager; } public static PowerManager getPowerManager(){ if(sPowerManager == null){ sPowerManager = (PowerManager) getContext().getSystemService(POWER_SERVICE); } return sPowerManager; } public static Display getDefaultDisplay(){ return ((WindowManager)getContext().getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); } public static String getABI(){ try { Field field = android.os.Build.class.getField("CPU_ABI"); String abi = field.get(null).toString(); if(abi == null){ return "unknown"; } return abi; } catch (Exception e) { e.printStackTrace(); return "unknown"; } } public static int getGlEsVersion(){ if(sGlEsVersion == 0){ sGlEsVersion = ((ActivityManager) NgnApplication.getContext().getSystemService(Context.ACTIVITY_SERVICE)).getDeviceConfigurationInfo().reqGlEsVersion; Log.d(TAG, "sGlEsVersion=" + sGlEsVersion); } return sGlEsVersion; } public static boolean isGlEs2Supported(){ return getGlEsVersion() >= 0x20000; } public static boolean isSLEs2Supported(){ return (NgnApplication.getSDKVersion() >= 9); } public static boolean isSLEs2KnownToWork(){ // FIXME: one-way audio on Galaxy SII and Nexus //return isSLEs2Supported() && Arrays.asList(sSLEs2FriendlyBuildModels).contains(sBuildModel); // AcceptOnlyIn(Array) //return isSLEs2Supported() && !Arrays.asList(sSLEs2UnFriendlyBuildModels).contains(sBuildModel); // AcceptAllExceptIn(Array) //return false; return true; //return isHovis(); //false; } public static boolean acquirePowerLock(){ if(sPowerManagerLock == null){ final PowerManager powerManager = getPowerManager(); if(powerManager == null){ Log.e(TAG, "Null Power manager from the system"); return false; } if((sPowerManagerLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG)) == null){ Log.e(TAG, "Null Power manager lock from the system"); return false; } sPowerManagerLock.setReferenceCounted(false); } synchronized(sPowerManagerLock){ if(!sPowerManagerLock.isHeld()){ Log.d(TAG,"acquirePowerLock()"); sPowerManagerLock.acquire(); } } return true; } public static boolean releasePowerLock(){ if(sPowerManagerLock != null){ synchronized(sPowerManagerLock){ if(sPowerManagerLock.isHeld()){ Log.d(TAG,"releasePowerLock()"); sPowerManagerLock.release(); } } } return true; } }