Call from foreground service stops working when screen is off - android

I have the following code that makes a phone call:
public static void CallPhoneNumber(this Context context, string phoneNumber)
{
var uri = Android.Net.Uri.Parse("tel:" + phoneNumber);
var callIntent = new Intent(Intent.ActionCall, uri);
callIntent.AddFlags(ActivityFlags.NewTask);
callIntent.AddFlags(ActivityFlags.FromBackground);
context.StartActivity(callIntent);
}
I make a phone call inside a running foreground service. Basically the service detects conditions (in my case GPS location) and makes a phone call. It worked just fine with my Pixel 2XL and Android 9. But after upgrade to Android 10 I faced to a new problem.
First of all, I was forced to add a new permission FOREGROUND_SERVICE. Added, the foreground service works as expected and makes phone calls - but only when phone is "active", I mean it is not in a "sleep" mode when the screen is turned off.
If the screen is off - the service works, I can track the activity, but it doesn't make a phone call.
The adb logcat shows this warning (first line is Info, the second is Warning):
02-04 20:48:00.923 1315 7951 I ActivityTaskManager: START u0 {act=android.intent.action.CALL dat=tel:xxxxxxxxxxxx flg=0x10000004 cmp=com.android.server.telecom/.components.UserCallActivity} from uid 10174
02-04 20:48:00.924 1315 7951 W ActivityTaskManager: Background activity start [callingPackage: MyApp; callingUid: 10175; isCallingUidForeground: false; isCallingUidPersistentSystemProcess: false; realCallingUid: 10174; isRealCallingUidForeground: false; isRealCallingUidPersistentSystemProcess: false; originatingPendingIntent: null; isBgStartWhitelisted: false; intent: Intent { act=android.intent.action.CALL dat=tel:xxxxxxxxxxxx flg=0x10000004 cmp=com.android.server.telecom/.components.UserCallActivity }; callerApp: ProcessRecord{43f3a72 13957:MyApp/u0a174}]

I think you should take a look at "partial wakelock" to prevent the "sleep mode" of the phone.
https://developer.android.com/training/scheduling/wakelock
in your MainActivity.cs create a WakeLock object and under your OnCreate function configure your wakelock :
private PowerManager.WakeLock _wl = null;
protected override void OnCreate(Bundle savedInstanceState)
{
// ... Your own code here
PowerManager pmanager = (PowerManager)this.GetSystemService("power");
_wl = pmanager.NewWakeLock(WakeLockFlags.Partial, "myapp_wakelock");
_wl?.SetReferenceCounted(false);
_wl?.Acquire();
}
public override void OnDestroy()
{
_wl?.Release();
base.OnDestroy();
}
Don't forget to add a permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.WAKE_LOCK" />
This solution can work but you're need to be carreful because for some manufacturer, if your app use to many ressources the foreground service responsible of the state "always alive" of the CPU can be killed. In some cases, they add a "battery saver" option mode and you need to disable it directly in settings to run your app without issues.
Hope this can help

Use TelecomManager. Something like this:
Uri uri = Uri.fromParts("tel", phoneNumber, null);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TelecomManager telecomManager = (TelecomManager) getInstance().getSystemService(Context.TELECOM_SERVICE);
telecomManager.placeCall(uri, new Bundle());
}
else
{
Intent callIntent = new Intent(Intent.ACTION_CALL, uri);
callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(callIntent);
}
}

Related

how my program stays awake all the time in the background in android studio

I am developing a program that like caller id. I have to keep it always awake also runing background.
Which permissions or flags that i need?
Also how can i get permission battery optimizaion close for my app
Thanks
for checking/disabling battery optimisation use below methods
public static boolean isBatteryOptimisationEnabled(Context context) {
if (Build.VERSION.SDK_INT >= 23) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
return pm != null && !pm.isIgnoringBatteryOptimizations(context.getPackageName());
}
return false;
}
#TargetApi(23)
private static void gotoBatteryOptimisationSettings(Context context) {
Intent intent = new Intent();
String packageName = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm == null || pm.isIgnoringBatteryOptimizations(packageName))
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
else {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
}
context.startActivity(intent);
}
and for keeping your app running whole the time you have to implement your logic in ForegroundService
You never can keep alive background services for always. if you want to make sure services always keep running you should use foreground service.
BUT
for your specific usage, you should use broadcast receivers to observe user calls.see this

Incoming call listener sleeps after couple of hours

For the last couple of weeks I am facing an issue with telephony manager API in Android - listener for incoming call based on listener starting Recording and on end call stopping recording (The process working smooth)
ISSUE
The issue I am facing is that in some mobile handsets it is working all the time, but in some handsets, Broadcast listener of telephony manager stops working after a few hours. After some research I found a solution that use wake-lock for preventing the CPU to sleep and I tried this but in vain.
#Override
public void onReceive(Context context, Intent intent) {
//We listen to two intents. The new outgoing call only tells us of an
//outgoing call. We use it to get the number.
roPlantPrefs = RoPlantPrefs.getInstance(context);
databaseHelper = new DatabaseHelper(context);
//lastState = roPlantPrefs.getLastState();
if (roPlantPrefs.getLogin()) {
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
} else {
roPlantPrefs = RoPlantPrefs.getInstance(context);
// if (!roPlantPrefs.getIsOnCall()) {
String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
int state = 0;
if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
state = TelephonyManager.CALL_STATE_IDLE;
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
state = TelephonyManager.CALL_STATE_OFFHOOK;
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
state = TelephonyManager.CALL_STATE_RINGING;
}
onCallStateChanged(context, state, number);
}
}
// }
}
I have also used timer and alarm manger but it is working maximum 2 to 3 hours then listener stops working, Any help could be appreciated.
I had same issue with Oppo, Vivo, Mi and etc phones, after removing from recent applications app was getting killed even services was getting killed
Solution: I had add autostart permissions like this in my application and it worked.
After resolving this issue my app was getting frozen/killed after some time running in the background due to DOZE mode
Solution: for this condition, just go to ->Settings ->Battery Option, and allow your app to run in the background, if you do this, DOZE mode wont affect on your app,
Cheers
try this intent of setting then put your application name (don't optimize)
for sdk 23 and up
Intent intent = new Intent();
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (pm.isIgnoringBatteryOptimizations(packageName))
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
else {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
}
startActivity(intent);

Prevent my android app starts automatically when the device/screen is sleeping/locked

The problem is that if my app is running and the device (screen) is locked, the app is restarted while the device is locked (I know because I can hear the sound of my app at startup).
[Edit]
This seems very complicated. I think it would be easier to turn off sounds in the app, but I do not know how to do this only when the device is asleep:
public void playSound(int id){
if(!DEVICE_IS_ASLEEP())
snds[id].play(soundID[id], 1, 1, 0, 0, 1);
}
you may registerReceiver using Context (probably inside Service)
//assuming user starting Service by press smth in app (or simple open it), so the screen will be on for sure
boolean screenOn=true;
//put this inside your onCreate
private void initBroadcasts(){
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
//new feature from API 17 - screensaver (?)
if(android.os.Build.VERSION.SDK_INT>=17){
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
}
screenOn = getScreenUnlocked();
this.registerReceiver(screenBroadcastReceiver, filter);
}
screenBroadcastReceiver is a BroadcastReceiver as below:
private BroadcastReceiver screenBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent myIntent) {
if(myIntent.getAction().equals(Intent.ACTION_SCREEN_ON))
screenOn=getScreenUnlocked();
else if(myIntent.getAction().equals(Intent.ACTION_SCREEN_OFF))
screenOn=false;
else if(myIntent.getAction().equals(Intent.ACTION_USER_PRESENT))
screenOn=true;
else if(android.os.Build.VERSION.SDK_INT>=17){
if(myIntent.getAction().equals(Intent.ACTION_DREAMING_STARTED))
screenOn=false;
else if(myIntent.getAction().equals(Intent.ACTION_DREAMING_STOPPED))
screenOn=getScreenUnlocked();
}
}
};
check if screen is unlocked:
private boolean getScreenUnlocked(){
KeyguardManager kgMgr =
(KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
return !kgMgr.inKeyguardRestrictedInputMode();
}
When configuration changes happens with app like screen lock, the activity is restarted. You will get so many answers on stackoverflow to avoid this problem like this; but according to Google Engineers, it is bad practice to retain the activity. You will get the proper answer about how to avoid this problem here

Android NotificationListenerService throws DeadObjectException

I have a simple NotificationListenerService implementation to test the new 4.3 API. The Service itself used to work. After that, I added the sending of a broadcast when a notification of a particular package is added. Now, as soon as I start the service, it throws a DeadObjectException. This is the stack trace:
E/NotificationService﹕ unable to notify listener (posted): android.service.notification.INotificationListener$Stub$Proxy#42c047a0
android.os.DeadObjectException
at android.os.BinderProxy.transact(Native Method)
at android.service.notification.INotificationListener$Stub$Proxy.onNotificationPosted(INotificationListener.java:102)
at com.android.server.NotificationManagerService$NotificationListenerInfo.notifyPostedIfUserMatch(NotificationManagerService.java:241)
at com.android.server.NotificationManagerService$2.run(NotificationManagerService.java:814)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at com.android.server.ServerThread.run(SystemServer.java:1000)
This is how I start the Service
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_start_service:
startService(new Intent(this, ConnectService.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
I can verify that the Service starts, because I do a Log on it's onCreate() and onDestroy().
And here is how the posting of notifications is handled, if it's needed:
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.i(TAG, sbn.getNotification().toString());
if (sbn != null && sbn.getPackageName().equalsIgnoreCase(PKG)) {
Intent intent = new Intent(ConnectService.NOTIFY);
intent.putExtra("notification", sbn.getNotification().toString());
bManager.sendBroadcast(intent);
}
}
The thing that sucks is that the stack trace is of no use. What's going wrong?
Try not starting the service yourself. If you have enabled the NotificationListenerService in the security settings, the system should bind to it automatically.
Alternatively, check your crash logs to see if your service crashed or its process was killed. I believe there is a bug where if your NotificaitonListerService dies, the system will not rebind until you restart your phone or toggle the notifications permission in security settings.
I would like to share my answer due the info I collected from different topics in stackoverflow and my own tests. If your NotificationListenerService fails (exception, like IllegalStateException), the system will kill it and not restore it again. You can see that in the logcat:
592-592/? E/NotificationService﹕ unable to notify listener (posted): android.service.notification.INotificationListener$Stub$Proxy#4291d008
android.os.DeadObjectException
....
If the user goes to Security, Notifications, disable and enable your app, it is still not working. Why? Because it will only by restarted if the user restart the phone or if the user re-enable the option BUT going thru your app using:
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
So we need to check two things, first if the option is enabled:
private boolean checkNotificationSetting() {
ContentResolver contentResolver = getContentResolver();
String enabledNotificationListeners = Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
String packageName = getPackageName();
return !(enabledNotificationListeners == null || !enabledNotificationListeners.contains(packageName));
}
If it is enabled, we check if the service is Death:
private boolean isNLServiceCrashed() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> runningServiceInfos = manager.getRunningServices(Integer.MAX_VALUE);
if (runningServiceInfos != null) {
for (ActivityManager.RunningServiceInfo service : runningServiceInfos) {
//NotificationListener.class is the name of my class (the one that has to extend from NotificationListenerService)
if (NotificationListener.class.getName().equals(service.service.getClassName())) {
if (service.crashCount > 0) {
// in this situation we know that the notification listener service is not working for the app
return true;
}
return false;
}
}
}
return false;
}
What is this service.crashCount ? Documentation says:
Number of times the service's process has crashed while the service is running.
So if its more than 0, it means it's already death. Therefore, in both cases, we have to warning the user and offer the possibility to restart the service using the intent I posted before:
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
Of course, if the service crashes, it will be good to detect why and when to prevent it too.

How to keep a foreground app running 24/7?

I am looking into how to keep my Android app running in the foreground.
It will be a privately distributed app, so I can do anything possible to make sure it runs constantly on the device (HDMI TV Stick)
So, how can I make sure that the app stays running no matter what? The app is pretty light weight in terms of resource usage, so having it run 24/7 should hopefully not be a problem.
I read about the persistent parameter in the manifest, but it looks like it might only apply to system apps?
Should I make my app a system app? How would I do that and would it help?
If you want an external app use: Autostart and StaY!
If you want to do this programmatically you can use a service that polls every "x" milliseconds to see if your app is in the foreground. If it is not, it will start/bring your app in the foreground. Do it like this:
public class PersistService extends Service {
private static final int INTERVAL = 3000; // poll every 3 secs
private static final string YOUR_APP_PACKAGE_NAME = "YOUR_APP_PACKAGE_NAME";
private static boolean stopTask;
private PowerManager.WakeLock mWakeLock;
#Override
public void onCreate() {
super.onCreate();
stopTask = false;
// Optional: Screen Always On Mode!
// Screen will never switch off this way
mWakeLock = null;
if (settings.pmode_scrn_on){
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "a_tag");
mWakeLock.acquire();
}
// Start your (polling) task
TimerTask task = new TimerTask() {
#Override
public void run() {
// If you wish to stop the task/polling
if (stopTask){
this.cancel();
}
// The first in the list of RunningTasks is always the foreground task.
RunningTaskInfo foregroundTaskInfo = activityManager.getRunningTasks(1).get(0);
String foregroundTaskPackageName = foregroundTaskInfo .topActivity.getPackageName();
// Check foreground app: If it is not in the foreground... bring it!
if (!foregroundTaskPackageName.equals(YOUR_APP_PACKAGE_NAME)){
Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage(YOUR_APP_PACKAGE_NAME);
startActivity(LaunchIntent);
}
}
};
Timer timer = new Timer();
timer.scheduleAtFixedRate(task, 0, INTERVAL);
}
#Override
public void onDestroy(){
stopTask = true;
if (mWakeLock != null)
mWakeLock.release();
super.onDestroy();
}
}
The above code has also the "option" to force the Screen to stay always on! Of course you will need the following permissions:
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
and do not also forget to register your service:
<service android:name="YOURPACAKGE.PersistService"
android:enabled="true"/>
use this:
import android.os.PowerManager;
public class MyActivity extends Activity {
protected PowerManager.WakeLock mWakeLock;
/** Called when the activity is first created. */
#Override
public void onCreate(final Bundle icicle) {
setContentView(R.layout.main);
/* This code together with the one in onDestroy()
* will make the screen be always on until this Activity gets destroyed. */
final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
this.mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
this.mWakeLock.acquire();
}
#Override
public void onDestroy() {
this.mWakeLock.release();
super.onDestroy();
}
}
And in the manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
Seen here: How do I keep the screen on in my App?
This is something not too easy to achieve as foreground apps are technically not supposed to be running non stop. Also if android is running out of memory it will start killing apps that pose the least risk which would then require the app to be restarted by the user.
As mentioned you could either make it a system app, but I think you do need to root the device or build your own ROM and make your app part of the ROM. Probably not the best solution for your needs though as few people will be able to flash a ROM on to their devices.
I think the easiest solution would be to put in the manifest that your app is a home screen replacement, i.e. a launcher app. I don't know the exact code from the top of my head but this would go into the application section within the android manifest. This would mean that as soon as the device boots, or the user presses the home button, they will be taken to your app.
I solved that issue by having a sticky service running that relaunches the app when the activity is getting closed.
//Your activity
#Override
public void onPause() {
super.onPause();
if (yourservice != null) {
yourservice.validateActivityOnPause();
}
}
and in the validateActivityOnPause() have something like:
//Your service
public void validateLynxActivityOnPause() {
//Do some stuff here
Intent startActivityIntent = new Intent(this, LynxActivity.class);
startActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(startActivityIntent);
}
Now that activityManager.getRunningAppProcesses() is deprecated( as of API21 ), you will want to replace :
RunningTaskInfo foregroundTaskInfo = activityManager.getRunningTasks(1).get(0);
String foregroundTaskPackageName = foregroundTaskInfo .topActivity.getPackageName();
with:
List<ActivityManager.RunningAppProcessInfo> tasks = activityManager.getRunningAppProcesses();
String foregroundTaskPackageNameTest = tasks.get(0).processName;
do not forget to import List with:
import java.util.List;
As a side note, I am not sure about OP's way of keeping the screen always on. I'm not sure that it works the way he's done it, but more importantly, it is deprecated and very much advised against to be using Wake Locks as you need to add permissions, which opens the door to bugs. Rather, it is generally better practice to use Window manager flags:
https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_KEEP_SCREEN_ON
You could make your app become a launcher, by adding 2 following category tags into <intent-filter> tags:
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.HOME"/>
then you should always check if there's another app run on top, run following code to direct user to our app:
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
I have tried this solution, but it cannot hide apps that draw on top, like Facebook Messenger chat head.
You may try startLockTask();
For More info, visit here

Categories

Resources