Tuesday, 9 June 2015

Fixing Manual Focus - A Cracker's way

Soon after providing find7 HAL for OnePlus One, people started complaining of broken RAW image capture functionality (i'm neither RAW image user nor any expert in camera sensor architecture). An obvious reason is find 7 HAL uses different format for RAW images, which seems broken (again i'm no expert here). Lots of formats are specified for RAW images. This can be easily fixed by tweaking camera to use desired (in our case, which is used by CM HAL) format. This is simple one line code using set(String, String) method of camera parameters. But this will require modification in all applications. I believe in providing full compatibility so i thought about modifying CM HAL to fix Manual Focus functionality.

I was aware that our HAL is same as find7's and thus it also supports Manual Focus because running dumpsys media.camera shows that min-focus-pos-index and max-focus-pos-index parameters are set by HAL. Professional mode plug in utilizes these two params (there is another one also). Also if you have noticed, when you slide MF slider from near to infinity and vice versa, you'll see focus moving, a little bit but it responds.

So, i again dumped camera parameters and checked values for min-focus-pos-index and max-focus-pos-index. It was 40 and 60 respectively. If you look at the code for MF Slider, it builds up slider in such a way that its value will jump between min-focus-pos-index and max-focus-pos-index. This made everything clear. Since slider was causing value to jump between 40 and 60 it had only a small effect on focus. Besides i loaded find7 HAL and checked values for these two params and it was 0 and 300 respectively.

So, conclusion is we need to change value of min-focus-pos-index from 40 to 0 and value of max-focus-pos-index from 60 to 300.

I haven't tested, but we can simply change values of these params by set method of camera parameters, but again it requires to be done from all applications so i left that way. All parameters are explicitly defined by HAL which can be overridden by Application Layer as per requirement. I started looking into QCOM Camera HAL source for understanding how they are utilized. All parameters related operations are defined under QCameraParameters.cpp (this one is from Nexus 6's HAL). If you look further, default parameters and values are set by initDefaultParameters() function. So now we know what we need to do. We need to hook into this function and set proper values for target parameters.

Note: This is not as simple as modifying smali code because of various reasons. It requires deep understanding of ARM architecture and assembly code. So be prepared with a cup of coffee :)

The real challange:
So let's start with patching HAL's blob. We'll use IDA PRO for analyzing/understanding code and HxD for modifying blob (hex editing). Here i assume you know how to use IDA (No worries if you don't. It's easy. You can comment here if you need any help) So, open up IDA PRO and load camera.vendor.bacon.so (our stock camera HAL, you can find it on /system/lib/hw).
As we already know we need to look for initDefaultParameters() function, search it from Function Window. Now double click and open up function.
Now let's find our target parameter keys. Press ALT+T to bring up search window and search max-focus-pos-index. Look for the match found at 0x0006169E and 0x0006168A for min-focus-pos-index. Notice that instruction to be executed after both these line is
BLX             _ZN7android16CameraParameters3setEPKci
This relates to call to function set(char const*,int). Obviously this is the place from where values for min-focus-pos-index and max-focus-pos-index are set. Now we need to find the address of the values which are being passed as parameter to this function. As we already know the value (40 & 60), we'll just search it (else you need to trace down parameter registers & function calls)
See the highlighted lines in screenshot just some lines above, it loads 0x28 (40) in R0 and 0x3c (60) in R1.
Conclusion: We need to replace 0x28 with 0x0 and 0x3c with 0x12c.
Let's start with replacing 0x28 with 0x0. Open up HxD and load camera.vendor.bacon.so. Now in IDA notice the line
.text:0006166A MOVS R0, #0x28
From this, we can see that offset is 0x6166A. Click on this line and open up Hex view-1 tab in IDA. You'll see two blocks high lighted. We are going to replace these two. Open up HxD and jump to given offset. We know that we want to replace this line with
.text:0006166A MOVS R0, #0x0
Here 20 is instruction code (Little endian format) for MOVS R0 and 28 is the Hex value to be stored. So replace 28 with 00 and its all done.
Now its turn for replacing 0x3c with 0x12c. This is not as simple as replacing 0x28 with 0x0 because each register can store number in range of 0-255 only. To store larger numbers we need to use wider instructions like MOV.W. MOV.W which are 4-byte instructions so 2-byte instructions like MOV can't be replaced by them. See STR.W is at 0x6166E. Such cases require use of Code Caves. Finding and utilizing code cave is hard and very complex job and requires huge amount of efforts. So for this tutorial i won't be including this topic. In next tutorial i'll write about how i achieved it.

If you want to try this by yourself, you can replace 0x3c with 0xFF (255) the same way we did with 0x28 to see practical effects. There is nothing wrong with it but focus will move between 0-255 in this case.

Hope i was able to make you understand everything.
You can share your thoughts, doubts, questions in comments :)

Have a nice day...


  1. Fantastic read! I'm hoping to learn enough to see if hardware HDR is possible on the OPO

    1. I see that in the nexus 6 HAL it's referred to as sensor HDR

    2. Unfortunately, no such thing for us...:(

    3. Aw man is it not defined in the HAL? I know the sensor is capable of it

    4. Yeah no such thing in our HAL. (FYI: Our HAL if from kitkat ages. HAL from find 7's LP beta is very much similar to N6's). Can't tell whether HDR on our device is tweaked image (same as software HDR but outcome is pre processed from HAL) or hardware HDR :(

    5. I wanna tsk a look at it if I ever find the time,, but I'm pretty sure it's software HDR. Hardware HDR should not need that long to process, maybe less than a second

  2. In this folder i don't have camera.vendor.codename.so i just have camera.msm8974.so. Does editing it is going to work?

    1. Ain't Mi4 has manual focus? (because my Mi3 had it)
      Anyways, yes you need to go for camera.msm8974.so. CM uses CameraWrapper for OnePlus One thus our HAL loads wrapper which communicates with actual HAL.

    2. Yes Mi4 has manuall focus too, but only with MIUI. Actually I'm searching a way to get almost same camera perf than MIUI but on lollipop.
      And Oppo Camera is really close that what MIUI camera gives (but without HDR).
      Thanks a lot for your reply. All you're doing is really impressive!

    3. If it does work on MIUI then you need to find out parameters to utilize same function on other ROM (when ROM uses same HAL as MIUI). Do this,
      1) Connect phone to PC
      2) Open any camera application (if possible, try this on MIUI & its stock camera app)
      3) In PC open up terminal and enter adb shell
      4) Get su rights and type dumpsys media.camera
      This will give you list of all parameters available/supported on you device. Find out which parameter related to manual focus.

    4. I just did it on MIUI and camera parameters are exactly the same as lollipop custom rom.
      Focus related parameters are exactly the same as Bacon device 40 and 60 for min and max.
      Si if I edit camera.msm8974.so the same way I should get manual focus working with oppo camera app :p

    5. You are a god :p
      Sincerly thanks a lot for your patience. I hope one day I will be as good as you for devleopment!

    6. So I'm here again, sorry for disturbance.
      I just well edited camera.msm8974.so. I replaced it in proper folder with proper permission and rebooted my phone.
      Manual focus remains the same (just moving a bit). But when I launch camera app and with adb "dumpsys media.camera" I get :
      min-focus-pos-index : 0
      mac-focus-pos-index : 255

      So HAL edition was sucessful but nothing changed from manual focus side.
      BTW I have min-focus-pos-dac : 510
      What does it means? Do you have the same?

    7. Many times vendors implement features (not directly supported by Android) in their own way and uses proprietary keys for it. It seems the case with MIUI. Check out Stock miui camera and see when you use manual focus which key is being used. On oneplus one, min-focus-pos-dac has value of 1023. I also don't know what it does stand for :(

    8. Oh that's sad, I de compiled miui camera app to search but I don't know what to look for or which class file to open. There are too much and ones related to focus are not really helpful.

    9. Oh that's sad, I de compiled miui camera app to search but I don't know what to look for or which class file to open. There are too much and ones related to focus are not really helpful.

  3. Is code obfuscated?
    If so the it'll take a while to figure out. Most common practice is that there will be a separate class for parameter management. Try to find class where all common camera parameters are used.

    1. Thanks a lot for your reply, but I guess most part of camera is in MIUI SDK (that would explain why HDR works with MIUI camera and not with other camera app even on stock MIUI rom, and why HDR is broken on all custom roms).
      In fact I just checked difference on manual focus with untouched HAL and with the on I edited. And result is :
      -Untouched HAL : Manual focus bar doesn't change focus at all
      -Modified HAL : Manual focus bar just change focus a little bit (can be sorted out in macro range)
      So edition is successful but normally I should get focus close to infinity.
      Maybe min-focus-pos-dac is somehow related to this weird comportment. (like a step calibration).
      But I can't find the hex value for this setting with IDA.

    2. As I see you have a MOVW value for this DAC which is 0x3FF (1023) with my IDA I don't see any MOVW that is 0x1FE (510)
      The only one I have is not close to focus settings and is a CMP.W :
      CMP.W R2, #0x1FE00

  4. Dont expect exact code because compiler optimizes code for best performance. Understand the logic. See what happens after comparision, in which subroutine it is present (it will give a bit idea of its functionality)
    Do this:
    Search for min-pos-blahblah. It will be mostly defined under read only data segment (preceded by .rodata)
    Right click over it and find xrefs to.
    Generated graph will give you idea about which functions utilizes it.

    1. Sorry I didn't get you. I can easily understand that code will be different. But I can't fin the .rodata (I used ARM in IDA of course).
      So as I can't find the .rodata for this parameter I can't find the xref (or maybe it is : .text:0005713C off_5713C DCD _ZN7qcamera17QCameraParameters24KEY_QC_MIN_FOCUS_POS_DACE - 0x56B9C
      .text:0005713C ; DATA XREF: qcamera::QCameraParameters::initDefaultParameters(void)+852 r)

      But that doesn't enlight me that much...
      I can't understand why focus has just that small move with 0-255 (255 should be focus close to infinity but still macro)

      Anyway edition worked but not as expected as focus was not moving at all before this edition.

    2. As you can see it is being referred in initDefaultParameters, check that function and see how it is used (mostly it'll be assigned some default value)

    3. Yes it returns to loc_56B7E (for me same location than min-focus-pos-index and max-focus-pos-index.

      Here is this location : http://pastebin.com/aLgnVds6 but there is no value for dac in it.

    4. Why are you trying to modify HAL when MF already work on your stock camera app. You just need to figure out how it works on your stock app, then implement missing calls to target app. For ex, on OPO we need to set manual-focus-pos-type & pro_camera_enable in order to make MF work. There may be some proprietary keys used in your stock camera also. Find out them and modify target app accordingly.

    5. In fact I'm not on MIUI anymore, I use custom lollipop rom that doesn't have such camera capability (output is generally blurry and overexposed pictures). And this Oppo app give as many function as MIUI camera and equal quality. In fact MIUI camera only works on MIUI, and porting it is too hard.

      I don't want to use MIUI anymore because it's laggy and too close to iOs in term of look.

    6. Yeah porting MIUI camera is hard because it requires huge changes in framework (like it requires extending some classes which android doesn't allow). I once tried it but left it when i realized that it
      'll require huge changes to framework which is impossible without root.

      About blurry and overexposed pictures, sometimes vendors implement their proprietary key in HAL to enable special features (like better de-noise algorithms). Same is the case with OPO, we supply addition parameter called "cyanogen-camera" from stock camera app. If this parameter is not supplied, zooming in recording will have aliasing effects (although in LP, this param is handled by camera wrapper so we don't have to care about it). Vendors use such tricks to prevent use of these features in third party camera apps. Same could be case with you.

      In any case, you need to find out how stock camera works (not whole thing, just figure out what,when and which parameters are being used)

    7. Okay, so even after trying I wasn't able to get more from HAL.
      Is there a way to change within the pro plug-in?

      I tried to change mMin and mMax values to be fixed to 40 and 60, but all I get is camera force close.

    8. @Richard: Unfortunately no because HAL is coded "that" way. Those parameters are not dynamic, means they won't have any effect upon change in value after initialization. Even if you will successfully change them, they'll be just shown changed, they won't have any effect (atleast not on OnePlus One, but most qcom devices share common code) but you should try it once. There should be no force close so it must be problem with your code.