CallScreeningService getExtras NULL on Android 10 API 29 - android

I am using the Android Class CallScreeningService onScreenCall(Call.Details calldetails) to get all incoming calls and everything works fine! From now I have an error, that on Android 10 devices the function calldetails.getExras() and calldetails.getIntentExtras() always returns NULL, instead of a Bundle, where I can read some further information. On Android 9 devices and older everything works fine.
Someone has a similar issue? Here is the Source Code and some Declarations:
public class IncomingCallService extends CallScreeningService {
#Override
public void onScreenCall(Call.Details callDetails) {
if (callDetails.getExtras() != null) {
Log.d(LOG_TAG, "Everything works on Android 9 or older");
}else{
Log.d(LOG_TAG, "Its Null on Android 10!");
}
if (callDetails.getIntentExtras() != null) {
Log.d(LOG_TAG, "Everything works on Android 9 or older");
}else{
Log.d(LOG_TAG, "Its Null on Android 10!");
}
}
And Manifest.xml:
<service android:name=".call_handler.IncomingCallService"
android:permission="android.permission.BIND_SCREENING_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallScreeningService"/>
</intent-filter>
</service>

According to Android docs:
Note: The Call.Details instance provided to a call screening service
will only have the following properties set. The rest of
the Call.Details properties will be set to their default value
or null.
Call.Details#getCallDirection()
Call.Details#getConnectTimeMillis()
Call.Details#getCreationTimeMillis()
Call.Details#getHandle()
Call.Details#getHandlePresentation()
Only calls where
the Call.Details#getHandle() Uri#getScheme() is PhoneAccount#SCHEME_TEL are
passed for call screening. Further, only calls which are not in the
user's contacts are passed for screening. For outgoing calls, no
post-dial digits are passed.
So what you are seeing it's just the expected behavior for Android 10.

On Androind 29+ you need user permission :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING);
startActivityForResult(intent, 1);
}

Related

AudioDeviceCallback fails when sharedUserId="android.uid.system"

I am developing an app for an Android custom build.
This app needs to subscribe to AudioDeviceCallback in Android's AudioManager.
I'm using:
mAudioManager.registerAudioDeviceCallback(new MyDeviceCallback(), null);
where:
private class MyDeviceCallback extends AudioDeviceCallback {
#Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
Log.d(LOG_TAG, "onAudioDevicesAdded(): New devices detected");
updateAuxStatus();
}
#Override
public void onAudioDevicesRemoved(AudioDeviceInfo[] devices) {
Log.d(LOG_TAG, "onAudioDevicesAdded(): devices removed");
updateAuxStatus();
}
private void updateAuxStatus() {
AudioDeviceInfo[] devices = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
boolean isPluggedIn = false;
for (AudioDeviceInfo device : devices) {
if (device.getType() == AudioDeviceInfo.TYPE_LINE_ANALOG) {
isPluggedIn = true;
}
}
onAuxPluggedInChanged(isPluggedIn);
}
}
This works fine when I run the app with normal user (u0_aXX) but when I run the app as system sharedUser (adding to the Manifest):
android:sharedUserId="android.uid.system"
This callback is being called only after subscribing, but never again. Even when other apps without system user are getting the call normally.
I've traced the call to the AudioManager code and found that postEventFromNative in the AudioPortEventHandler is never being called for my app when it is running as system sharedUser. Since that is a jni call I stopped because I don't fully understand how it works.
What really troubles me is that when running without system sharedUser the same code is working as intended. Is there some restriction to system sharedUser that might be causing this problem?
In the AudioPolicy, registerClient use uid as the key. When the phone booted, system_server or telecom already register notification, if your app run as system user, it will never working.
mNotificationClients.add(uid, notificationClient);

Testing Xamarin Geofence prototye in emulator fails

I have a prototype application that uses Geofencing set up in AndroidStudio and have been able to succesfully test it in the Android Emulator. Because I need the application to also be iOS i have ported the prototype to Xamarin/Visual Studio 2017 to make sure that it works in that environment so I can save myself from having to code the core logic of the app in Android and iOS. However I am not able to get the Geofences to fire in the Xamarin based app on the same emulator. Has anyone worked with this technology in Xamarin? are there specific settings that need to change for Xamarin to make this work?
The issue is probably coming from the manifest.
In Xamarin, when you create a service (or intent service) it should be tagged with the attribute [Service], instead of adding it to the manifest manually.
You should also check for errors when handling the intent (in case you are not doing it already):
[Service]
public class GeofenceTransitionsIntentService : IntentService, IEnableDatabaseLogger
{
public GeofenceTransitionsIntentService()
: base(nameof(GeofenceTransitionsIntentService)) { }
protected override void OnHandleIntent(Intent intent)
{
base.OnHandleIntent(intent);
this.Log().Info("Intent received");
var geofencingEvent = GeofencingEvent.FromIntent(intent);
if (geofencingEvent.HasError)
{
var errorMessage = GeofenceErrorMessages.GetErrorString(this, geofencingEvent.ErrorCode);
this.Log().Error(errorMessage);
return;
}
var geofenceTransition = geofencingEvent.GeofenceTransition;
var geofences = geofencingEvent.TriggeringGeofences;
var location = geofencingEvent.TriggeringLocation;
if (geofenceTransition == Geofence.GeofenceTransitionEnter)
{
foreach (var geofence in geofences)
this.Log().Info($"Entered {geofence.RequestId} at {location.Latitude}/{location.Longitude}");
// do something
}
else if (geofenceTransition == Geofence.GeofenceTransitionExit)
{
foreach (var geofence in geofences)
this.Log().Info($"Exited {geofence.RequestId} at {location.Latitude}/{location.Longitude}");
// do something
}
else
{
this.Log().Error($"Geofence transition invalid type: {geofenceTransition}");
}
}
}
Here is a demo (working) project I did recently: https://github.com/xleon/geofencing-playground

Android N call blocking numbers not getting

I am trying to get the call block numbers in android N, i want to know the given is block number or not (ex:- 5554 emulator number)
Contacts, sms, phone state permissions has been given to allow to access the block numbers and i followed the "Android Developer" site https://developer.android.com/reference/android/provider/BlockedNumberContract.html
But i am unable to get the block numbers, i am using latest android studio 2.2.2 and checked the functionality in android N emulator i don't have device.
Here is my code.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// Button onclick method to show the logs
public void displayBlockCursorCount(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
String number = "5552";
if (BlockedNumberContract.canCurrentUserBlockNumbers(MainActivity.this)) {
if (BlockedNumberContract.isBlocked(MainActivity.this, number)) {
Log.e(TAG, "given number is blocked >>>>>> " + number);
}
}
}
}
}
I am getting the
java.lang.SecurityException: Caller must be system, default dialer or default SMS app.
Please post the comment if down comment and thanks for advance.
To access blocked contacts,Your app should be default calling app or Messaging app else it throws security exception.
Add additional check
private boolean isAppAsDefaultDialer() {
TelecomManager telecom = mContext.getSystemService(TelecomManager.class);
if (getApplicationContext().getPackageName().equals(telecom.getDefaultDialerPackage())) {
return true;
}
return false;
}
or check sources https://android.googlesource.com/platform/packages/providers/BlockedNumberProvider/+/android-7.0.0_r1/src/com/android/providers/blockednumber/BlockedNumberProvider.java
And make you app as defaul dialer
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="tel"/>
</intent-filter>

Get Notification about new/changed/deleted contacts in Android when App is not running

it there a way to get noticed if a new or changed contact is made in Android? I want to get notified when the app starts, if there are any changes. Using a ContentObserver seems to me, that the app must run it in a activity. Or do i have to load all contacts every time from my DB and i am only able to recognize contact changed while my app runs and has an implemented ContentObserver?
i am only able to recognize contact changed while my app runs and has an implemented ContentObserver?
Correct, at least through Android 6.0.
The N Developer Preview has an enhanced JobScheduler that implements a ContentObserver for you, invoking your JobService when a change is detected. Unless there are problems, we can expect that enhanced JobScheduler to ship in the next release of Android, and you can opt into using it on newer Android devices.
Ok, what i did now is: Using a background service and build up an ContentObservice in the onCreate() function. Finally declaring it in the manifest. It will of course not work if the App is totally closed but if it is in background. Thats enough for me. It detects changes to the contacts. Are there any disadvantages in using this approach?
This is the service:
public class ContactsChangeService extends IntentService {
/**
* An IntentService must always have a constructor that calls the super constructor. The
* string supplied to the super constructor is used to give a name to the IntentService's
* background thread.
*/
public ContactsChangeService() {
super("ContactsChangeReceiver");
}
#Override
public void onCreate() {
super.onCreate();
//if created make an Observer
ContactsChangeObserver contentObserver = new ContactsChangeObserver();
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contentObserver);
Log.d(Constants.TAG, "[" + Constants.CONTACTS_OBSERVER_SERVICE + "] " + "started");
}
#Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
//...
// Do work here, based on the contents of dataString
//...
}
}
This is the Observer:
public class ContactsChangeObserver extends ContentObserver{
public ContactsChangeObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(Constants.TAG, "[" + Constants.CONTACTS_OBSERVER_SERVICE + "] " + "Change in Contacts detected");
}
}
And this is the manifest entry:
<service
android:name=".service.ContactsChangeService"
android:exported="true">
</service>

How to detect that the call which is done from our android device is answered or rejceted?

I am making an application to call multiple numbers.
In that app
When I call to 1 person and if the call is answered by the user then
the loop should be stopped.
But If the call is rejected then the call should be on next number and
loop should be couninue.
My problem is I cant detect whether the call is rejected or answered. when I had search on net some people says it is not possible to detect the call is answered or rejected.
Is it really not possible to detect the call in android If it is possible then how can I do that?
I think you can check outgoing call time of last call in PhoneStateListener class' onCallStateChanged method. Fetch the data if state is idle that is TelephonyManager.CALL_STATE_IDLE.
Something like this:
Cursor mCallCursor = context.getContentResolver().query(android.provider.CallLog.Calls.CONTENT_URI,null,null,null,null);
int duration = mCallCursor.getColumnIndex( CallLog.Calls.DURATION);
while(mCallCursor.moveToFirst())
{
Toast.makeText(context, mCallCursor.getString(duration), Toast.LENGTH_LONG).show();
}
You can find more about that here. I haven't tested the above code. But something like that should work.
You can check if time's 00:00, then call next number of loop. Else you can stop calling.
Hope this helps you.
below is a code of detecting outgoing call by accessibility events -
Add a class which extends AccessibilityService in your projects -
public class CallDetection extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
acquireLock(this);
Log.d("myaccess","after lock");
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
Log.d("myaccess","in window changed");
AccessibilityNodeInfo info = event.getSource();
if (info != null && info.getText() != null) {
String duration = info.getText().toString();
String zeroSeconds = String.format("%02d:%02d", new Object[]{Integer.valueOf(0), Integer.valueOf(0)});
String firstSecond = String.format("%02d:%02d", new Object[]{Integer.valueOf(0), Integer.valueOf(1)});
Log.d("myaccess","after calculation - "+ zeroSeconds + " --- "+ firstSecond + " --- " + duration);
if (zeroSeconds.equals(duration) || firstSecond.equals(duration)) {
Toast.makeText(getApplicationContext(),"Call answered",Toast.LENGTH_SHORT).show();
// Your Code goes here
}
info.recycle();
}
}
}
#Override
protected void onServiceConnected() {
super.onServiceConnected();
Toast.makeText(this,"Service connected",Toast.LENGTH_SHORT).show();
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
info.notificationTimeout = 0;
info.packageNames = null;
setServiceInfo(info);
}
#Override
public void onInterrupt() {
}
}
But to get the function event.getSource() working you have to specify some of your service configuration through xml, so create a xml folder in your project and add a xml file called serviceconfig.xml (you can give any name you want.
The content of serviceconfig is below -
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/callDetection"
android:accessibilityEventTypes="typeWindowContentChanged"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>
You can find more about serviceconfig in Here
Now add your service in you Manifest file like this -
<service android:name=".CallDetection"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:label="#string/callDetection">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/serviceconfig" />
</service>
And youre done, just run the app and go to Accessibility settings in your phone, you will find an option named as detection (or whatever name you have given as your service description), switch that on to give accesibility permissions for you app.
Now you will see a toast when call is answered.
you can Code any code you want in there, also you can call a callback function in your activity
Most important - Dont call your call window(android dialer window) untill the call is answered, otherwise this will not work.
Note - As android doesn't provide any solution to detect if the call is answered or not, this is the best alternative i have made, hope it works for you.

Categories

Resources