TheLastProject / RaiseToAnswer

Simply hold your phone to your ear to answer an incoming call
https://play.google.com/store/apps/details?id=me.hackerchick.raisetoanswer
MIT License
72 stars 13 forks source link

App not compatible with Android 12 #89

Open TheLastProject opened 2 years ago

TheLastProject commented 2 years ago

After updating targetSdkVersion to 31, the app crashes on an incoming call.

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: me.hackerchick.raisetoanswer, PID: 6271
    java.lang.RuntimeException: Unable to start receiver me.hackerchick.raisetoanswer.RaiseToAnswerCallReceiver: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service me.hackerchick.raisetoanswer/.RaiseToAnswerSensorEventListener
        at android.app.ActivityThread.handleReceiver(ActivityThread.java:4357)
        at android.app.ActivityThread.access$1600(ActivityThread.java:256)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2101)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7842)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
     Caused by: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service me.hackerchick.raisetoanswer/.RaiseToAnswerSensorEventListener
        at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
        at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
        at android.os.Parcel.readParcelable(Parcel.java:3333)
        at android.os.Parcel.createExceptionOrNull(Parcel.java:2420)
        at android.os.Parcel.createException(Parcel.java:2409)
        at android.os.Parcel.readException(Parcel.java:2392)
        at android.os.Parcel.readException(Parcel.java:2334)
        at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:6028)
        at java.lang.reflect.Method.invoke(Native Method)
        at leakcanary.ServiceWatcher$install$4$2.invoke(ServiceWatcher.kt:93)
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
        at $Proxy3.startService(Unknown Source)
        at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1847)
        at android.app.ContextImpl.startForegroundService(ContextImpl.java:1823)
        at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:779)
        at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:779)
        at me.hackerchick.raisetoanswer.Util$Companion.startSensorListener(Util.kt:176)
        at me.hackerchick.raisetoanswer.RaiseToAnswerCallReceiver.onReceive(RaiseToAnswerCallReceiver.kt:18)
        at android.app.ActivityThread.handleReceiver(ActivityThread.java:4348)
        at android.app.ActivityThread.access$1600(ActivityThread.java:256) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2101) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7842) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 
     Caused by: android.os.RemoteException: Remote stack trace:
        at com.android.server.am.ActiveServices.startServiceLocked(ActiveServices.java:692)
        at com.android.server.am.ActiveServices.startServiceLocked(ActiveServices.java:617)
        at com.android.server.am.ActivityManagerService.startService(ActivityManagerService.java:11876)
        at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2542)
        at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2516)
TheLastProject commented 2 years ago

This will prevent updating on Google Play starting November 1st, 2022 and completely block new installs on Google Play starting November 1st, 2023 as per https://support.google.com/googleplay/android-developer/answer/11926878.

https://github.com/TheLastProject/RaiseToAnswer/issues/34 might be the best fix.

dinesh-thiyagarajan commented 1 year ago

Hey I think I can help you with this issue, can you assign this to me ?

TheLastProject commented 1 year ago

Sure, good luck! I've gotten fairly stuck with this issue myself so hopefully you'll fare better :)

dinesh-thiyagarajan commented 1 year ago

Hey @TheLastProject I ran the app and tested in Android 13 but the app is not crashing.Tried for both background and foreground mode as well. Its working well :)

TheLastProject commented 1 year ago

The problem is that it's running in compatibility mode (targetSdk 30, so Android 11). Running it natively on Android 12 (by increasing targetSdk in app/build.gradle) makes it crash. Running in compatibility mode, even though it works fine, is not allowed on Google Play and will get the app removed (see the link posted above). It needs to be made to natively work with newer versions.

dinesh-thiyagarajan commented 1 year ago

Cool got it !!

dinesh-thiyagarajan commented 1 year ago

Hey, I tried converting the foreground service into work manager due to the foreground service restrictions on android 12, but workmanager comes with certain conditions to be satisfied for the system to allow it to keep running, ex: battery level, charging status etc.. which won't be suitable for our case in which the work needs to run indefinitely and keep listening to incoming calls.

And as you mentioned implementing the IncallService will be a good idea, but then that needs the app to be converted to a dialler app. which must fully implement the InCallService API and provide both an incoming call UI, as well as an ongoing call UI. Im not sure if that's what you wanted to do. @TheLastProject

TheLastProject commented 1 year ago

So there's no way to start a workmanager when the PHONE_STATE intent gets delivered?

Having to build a full new UI seems like quite overkill and like it will just make the app worse for everyone. If you feel like it you're free to try and I'll take a look and mark it hacktoberfest-accepted if the code is decent, but I'm not quite sure if I'll release it (maybe just keep it ready until it's about to get kicked out of Google Play and release it on F-Droid as a non-recommended version)

I guess Google has really actively killed the one API that made apps like this useful :(

dinesh-thiyagarajan commented 1 year ago

@TheLastProject We can start the work manager immediately once we receive the broadcast which is working fine while the app is in the foreground (tested in android 13 as well 😃), but the problem is once the app goes into the background according to the documentation

On devices running Android 9 (API level 28) or higher, apps running in the background have the following restrictions:

Sensors that use the continuous reporting mode, such as accelerometers and gyroscopes, don't receive events. Sensors that use the on-change or one-shot reporting modes don't receive events. Given these restrictions, it's best to detect sensor events either when your app is in the foreground or as part of a foreground service.

So we are not receiving on-change events if the app is in the background. I tried setting up the workManager with setForeground method, but that as well didn't work :( will see if its possible to get sensor events inside a workManager while app being in the background and raise a PR if it works

TheLastProject commented 1 year ago

If moving to the foreground doesn't work, I guess it would be possible to keep a "Raise To Answer is watching for phone calls" notification visible 24/7 and explain users when they tap it why it exists. It sucks both UI-wise and battery usage-wise (because now Raise To Answer has to run 24/7 instead of activating when a call comes in), but well, this is what Google has forced upon apps then.

dinesh-thiyagarajan commented 1 year ago

@TheLastProject Hey have raised a PR for this bug with a fix.

By asking the user to exclude the app from battery optimisations, which enabled the existing foreground service to be initiated from the background. I feel this maybe a better fix since we don't keep the service running indefinitely in the background and only start it on receiving the broadcast and stop it on pickup or decline.

however there is a catch

Google Play policies prohibit apps from requesting direct exemption from Power Management features in Android 6.0+ (Doze and App Standby) unless the core function of the app is adversely affected.

If google reaches out we should be able to explain that without this fix the core functionality of the app will be affected, you can check the documentation here for details. and these are the acceptable usecases where we can use ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS

I hope this helps :)