Magisk module for (system-)camera apps

For those who would like to try (and don’t start from scratch) or extend it or just play around, I put the module dummy to git: Git - CamAsSystemApp dummy
(It is just a dummy and does not include the APK/Folder and Files of FPCamera → it’s too big for git)

I debugged that issue. Problem is that for one of the cameras the HAL for the camera returns an “invalid” value (CameraCharacteristics.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS) . That then causes an exception when opening that Camera via Camera2 Java APIs and makes the App crash :-/

Prior to Android 12 that value wasn’t accessed when opening the camera.

It’s a one line fix in android_frameworks_base/CameraMetadataNative.java at lineage-19.1 · LineageOS/android_frameworks_base · GitHub to prevent it.

2 Likes

Awesome, I’m happy everything worked for you :smiley:

Somebody on the forum discovered it some time ago. There are answers for all the problems on this forum, I’m pretty sure we’ll find the answer to life, the universe and everything at some point in the future :smirk:

There are certainly some libraries missing, you would have on a stock install. The normal mode also lets you select several filters, my guess is that those might be crashing it, but I haven’t had the time to look at it really. Edit: Well, that happens if you take ages to write an answer, someone will provide the actual one :smile:

Mainly if everything worked fine with the permissions.xml and the SYSTEM_CAMERA permission was not only requested…

FP4:/ $ dumpsys package com.fp.camera | grep SYSTEM_CAMERA                                                                                           
      android.permission.SYSTEM_CAMERA
      android.permission.SYSTEM_CAMERA: granted=true

…but also actually granted.

Am I understanding you correctly that this workaround wouldn’t be necessary if the camera app was actually meant for A12, since it can’t handle that behaviour, or is there something else going on I’m not getting?
Should this be fixed in the FP Camera app once the FP4 officially moves over to 12?

I believe it’s not the fault of the App in this case. The app tries to open Camera ID 3 (BACK_SAT) when in normal photo mode. Camera with id “3” apparently is a logical camera - not sure what the actual purpose of this it all.

Logical cameras are supposed to have physical sub cameras. Now with updated framework code when opening the camera it tries to load the characteristics of the physcial subcameras. But the list of subcameras which is provided via the HAL is not well-formed. That’s supposed to be a byte array where the camera ID strings are separated by ‘\0’ bytes. But here for Fairphone it’s [‘\0’, ‘\0’, ‘0’, \0’].

That is then split into list of physical cameras [ “”, “”, “0” ]. Loading characteristics of unidentified Camera “” of course fails and throws the exception. That is all framework-internal.

We also see this in the log pasted above by @Smojo:
java.lang.IllegalArgumentException: getCameraCharacteristics:754: Unable to retrieve camera characteristics for unknown device : No such file or directory (-2)

So we can fix it to handle that data gracefully. For C++ Native Camera APIs that is actually filtered, but not on Java API side. The actual fix would be to provide a well-formed list of subcameras. That would probably be somewhere in closed source vendor camera blobs …

3 Likes

Can you implement that fix or is it already worked on by the LineageOS devs and could we bring that into CalyxOS? :slight_smile:

So we need to have some Fairphone developers take a look at the findings in this thread. Great work guys!

So next step (or more a possibility) here would be testing Open Camera. I’m still not sure if I have the knowledge/skills to patch an APK … (even it sounds simple ^^)

but if I would just use the /e/OS Camera (which is a port of OpenCamera) the simple Magisk module approach, I used for the FPCamera (extract from factory zip etc. …), should work as well, or am I wrong?

I haven’t tried it, but that would have been my suggestion as well.
Why patch it if you can get one that should work already :slightly_smiling_face:

Update: Well, crashes on start with the following error:

08-27 17:34:57.861  8307  8307 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{foundation.e.camera/net.sourceforge.opencamera.MainActivity}: android.view.InflateException: Binary XML file line #285 in foundation.e.camera:layout/activity_main: Binary XML file line #285 in foundation.e.camera:layout/activity_main: Error inflating class android.widget.Button

So line #285 in this file, just a button, no idea what this means :thinking:
No time to debug this further at the moment, but if it isn’t an easy fix, building OpenCamera with the proper permissions might be the easier route after all :man_shrugging:

Full stack trace
--------- beginning of crash
08-27 18:08:29.801 16852 16852 E AndroidRuntime: FATAL EXCEPTION: main
08-27 18:08:29.801 16852 16852 E AndroidRuntime: Process: foundation.e.camera, PID: 16852
08-27 18:08:29.801 16852 16852 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{foundation.e.camera/net.sourceforge.opencamera.MainActivity}: android.view.InflateException: Binary XML file line #285 in foundation.e.camera:layout/activity_main: Binary XML file line #285 in foundation.e.camera:layout/activity_main: Error inflating class android.widget.Button
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3707)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3864)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.os.Looper.loopOnce(Looper.java:201)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:288)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:7870)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: Caused by: android.view.InflateException: Binary XML file line #285 in foundation.e.camera:layout/activity_main: Binary XML file line #285 in foundation.e.camera:layout/activity_main: Error inflating class android.widget.Button
08-27 18:08:29.801 16852 16852 E AndroidRuntime: Caused by: android.view.InflateException: Binary XML file line #285 in foundation.e.camera:layout/activity_main: Error inflating class android.widget.Button
08-27 18:08:29.801 16852 16852 E AndroidRuntime: Caused by: java.lang.reflect.InvocationTargetException
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at java.lang.reflect.Constructor.newInstance0(Native Method)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.createView(LayoutInflater.java:858)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.createView(LayoutInflater.java:780)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at com.android.internal.policy.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:58)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.onCreateView(LayoutInflater.java:934)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.onCreateView(LayoutInflater.java:954)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1008)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.rInflate(LayoutInflater.java:1127)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.inflate(LayoutInflater.java:686)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.view.LayoutInflater.inflate(LayoutInflater.java:485)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:461)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.Activity.setContentView(Activity.java:3526)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at net.sourceforge.opencamera.MainActivity.onCreate(MainActivity.java:224)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.Activity.performCreate(Activity.java:8057)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.Activity.performCreate(Activity.java:8037)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1345)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3688)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3864)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.os.Looper.loopOnce(Looper.java:201)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:288)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:7870)
08-27 18:08:29.801 16852 16852 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: Caused by: java.lang.UnsupportedOperationException: Failed to resolve attribute at index 6: TypedValue{t=0x2/d=0x101009b a=1}
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	at android.content.res.TypedArray.getColorStateList(TypedArray.java:598)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	at android.widget.TextView.readTextAppearance(TextView.java:4062)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	at android.widget.TextView.<init>(TextView.java:1088)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	at android.widget.Button.<init>(Button.java:166)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	at android.widget.Button.<init>(Button.java:141)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	at android.widget.Button.<init>(Button.java:117)
08-27 18:08:29.802 16852 16852 E AndroidRuntime: 	... 33 more

Hm :-/ if I start patching APKs it might be an option to try to remove this line.

I will start reading a bit about building an APK from it’s code (how I get the tools I need etc. …).

I first started to use nix-env, which might not be the very best way to use it.
Short video why: Select a Invidious Instance - video: " Is nix-env bad?"

Great little article about nix and nix shell: Working with nix-shell - SmartJava

As I will probably only use this from time to time I think something like setting up a
nix-shell android-tools.nix
is the better way and out in all the packages I need for the things discussed here (e.g. also gradle can be added).

I’ve been reading along with great interest since the beginning and wanted to ask if you’ve made any progress.

I’ve had absolutely no time to experiment further in the last two weeks, not sure about the others… :thinking:

2 Likes

I was able to build my own OpenCamera with SYSTEM_CAMERA permission according to @xblax’s instructions.
I also successfully created a Magisk module using @Smojo’s dummy.

After installing, dumpsys reports that SYSTEM_CAMERA permission has been granted.

Unfortunately it doesn’t seem to pick up any additional cameras.
However, I am using the stock Fairphone OS, so that could be an issue, I guess.

Here is the Magisk module, in case anyone wants to try it on a different OS.

8 Likes

Did you switch OpenCamera to the Camera2 API (Settings → Camera API, restart the app)? :thinking:

I’ve tried your module on CalyxOS and the wide-angle lens works fine :tada:
Great job :metal:

Oh, and welcome to the community BTW :wave:

2 Likes

Could we use our WeAreFairphone-Git for getting this to the masses :)?

Yes, otherwise there is nothing to change, right?
OpenCamera’s About in Settings also reports MultiCamera?: false.

I’m glad it works for you :slight_smile:

Thank you very much.

Feel free to publish it wherever you want, at least from my side.
I am not sure if it would violate any of OpenCamera’s licenses, though.

Welcome @maxmitti and nothing to add to @hirnsushi first reply to you. → Great job! :wink:

I didn’t had the time yet to work on this any further. Still miss some build tools to create my own APKs out of modified code (as most of us here I’m not a developer). Let’s see when I have time to play around with this again. I will try your module for now - so thanks again.

Also I saw this in the GCAM Port channel: https://www.celsoazevedo.com/files/android/p/camera-unlocker-oneplus-8-9r/ when I read these points it feels a bit like they fight the same fight. ^^

Hey !
I recently got a Fairphone 2, and needed to have Opencamera as my default on Lineageos 18.1, and this works perfectly !
Thank you very much for your work :blush:

so… if one were to try the same with a GCam port instead of OpenCamera, would it be a matter of just swapping the file in the zip and changing package names in the .xml, or is it strictly for that particular camera app?

The last time I checked, the GCam version I’m using (MGC_8.6.263), didn’t have SYSTEM_CAMERA declared in the AndroidManifest.xml.
You would at least need to patch that in with something like ApkTool.

But there are other hurdles as well, apart from the 3rd lense, like slow motion video, long exposures, 48mpx, etc.…
I’ve wanted to try to push this further for quite some time, but honestly, I’m fine with the camera (at the moment), seems like a task for those cold dark winter days :smirk:

3 Likes