 Hey guys, good evening. This is Modit. I am VP Engineering at Hansel.io. We are a command center for your live mobile apps. We let you change your UI. We let you diagnose the issues in the live app. We let you fix those issues by which I mean we let you fix your crashes. We let you fix your logical issues. We let you change the behavior in certain cases. So do check us out. But then today it's not about us. It's about you guys. I'll be letting you build a simple version of Hansel.io, which will let you do the same things for yourself. So as mobile developers, we work in an environment which is constrained by the memory, low memory, limited disk space, low battery power, bad network, which is especially in India. We have bad network. And then the devices, the kind of devices that are there in the market, they make the whole ecosystem quite fragmented. You have devices with different screen sizes. You have devices with different form factors. Devices with different kind of processing powers, low memory. And the way these handset manufacturers give out the updates for the new APIs, that adds to more fragmentation every year as Google releases another version of the API. Beyond fragmentation, since we're developing for apps, not for web, the release cycle for us is quite long. We develop the app, then we give it to the QA, QA does its own set of testing for the version, for the feature that you're releasing, then do a regression testing. And then by the time it goes out, it even for a smaller feature, it's a matter of days. Now, so you created an app, you released it. As it goes out in the wild, then it gets tested. So you tested on a certain set of devices, and the app will work fairly well. But in certain cases, when the device is running on a low end, or say some new device crops up in the market, the APK that you've released starts to behave not in the way that you wanted to behave. You start facing logical issues, by which I mean the UI doesn't work well, the performance is not good enough. And then the most dreaded happens, you have crashes. The problem with, why does it happen? So you never provisioned for a better version of Android, which has gone live a month back. You probably forgot to add some permissions. You probably didn't provision for your app getting installed on an external SD card. And of course, you made some changes in the code, which you might want to call them features, but they are bugs. So because of all this, the app starts to crash. So all this leads to a lot of revenue loss, bad ratings. You have ranks on Twitter and Facebook. And then the loss of users. So I was discussing with the gentleman outside the presentation and he had a different insight to it. What this guy told me that there is a study which tells you that there are only five or seven apps, which a user uses. And beyond that, all the apps which are there on your phone, they are there, but they are waiting to get uninstalled. So if any of these things happen, you lose the users. So what do we need as developers? We cannot afford to lose users because as soon as you lose a user, you are losing revenues in the longer run. So basically what we need is reduction in the response time to any issue in the app. If there is a crash or if there's a logical issue, what you need is a new app going live as soon as possible. But the way the app ecosystem works, this is not a matter of minutes. It's a matter of days. Until then, the user which is having your app is getting frustrated. It has the old app which doesn't work at all. It crashes or it behaves in a way which you don't want the user to see. So what we need is a tool which can help us diagnose the issue really fast and not just diagnose the issue. We need something which can fix these issues in real time. By issue, I meant logical issues that you're facing and of course the crash and all that, of course, on the live app in real time. There are certain tools which do help you do this. These are crash reporting tools. A lot of you must have been using one of these tools. Crashlytics have been around for quite some time. It's from Fabric. Then a lot of people use Hockey app. Firebase is the new kid in the market. But it's catching up. Then you have criticism. But all these tools have multiple issues. They are more of a reporting tool. They let you know what happened in a really objective manner. They let you know that a crash happened and this is the stack trace. They won't let you diagnose the issue. They won't let you know why this crash happened. They don't talk about anything related to logical issues. And when you start diagnosing an issue, there are certain cases wherein you need to know the flow because of which this crash happened. So I'll give you an instance. As in one of my previous companies, there was an issue in the login page. We tested it a lot and we tried to reproduce the issue a lot. But after two, three days, we figured out that this issue was not in the login page directly. But it was when people used to come to login page through a special flow. So what I'm trying to say over here is like we wasted two, three days of developer time just to figure out that there is a crash happening on a login page. We could have figured it out easily if we had known what the flow is through which these users are getting these issues. So yes, none of the tools that I mentioned let you do that. Let's you do that. So we are back to the same state. We need a tool which does fast diagnosis of the issues for us and let's us solve these issues in real time on the live app. Is there anything available? Yes, there is. It's called Hansel.io. But of course we won't be talking about that here. I'll be enabling you to create something similar to what we have built. I'll be letting you create the miniature version of Hansel. So let's go back to the topic. What I was telling you, so what I started with was instrumentation and how instrumentation works in Android. So let me tell you what instrumentation is. Most of us might have used it, but let's go to the root of it. So instrumentation is addition of bytecode to the existing methods. And in most of the cases, we have used it to gather data. I'll tell you the instances where we have already used this concept. In Android Studio, you must have seen Android Monitor. You must be using it. So Android Monitor lets you monitor the performance of the app which you're about to ship. During the development time, it lets you figure out how much network you're using, what is the CPU usage, what is the GPU usage, and how much memory your app is consuming. So this tool works on the concept of instrumentation. Beyond this, these are the other tools. We have Dex Maker, we have ASM Dex. And there is a really beautiful tool called Aspect J. So today we'll be using a tool called Aspect J. It's a part of Eclipse Foundation. It's an open source tool. And we'll be using it to create what we are intending to create. So Aspect J works on the concept of aspect-oriented programming. Aspect-oriented programming. We have all been working over Java or C++ since we're all Android developers. So in any object-oriented programming, each class has a primary function. So say you have a VC. So wherein you have models, then you have views. They have their own predefined functionality. Models have to keep the data. Views have to display the data. View is the display component. But say you want to figure out the performance of each function inside a model or the performance of each function inside your view. So what you would do is you'd start logging these functions. So logging is a feature which is being shared by both these components. Aspect-oriented programming lets you modularize this logging part in a separate concern. So each of them is a concern. As in your model is a concern, your view is a concern, and logging is a different concern which is being used by these two concerns. So logging is called a cross-cutting concern. Now the code which you write inside logging is called advice in the case of Aspect AOP, Aspect-oriented programming. So the way Aspect-oriented programming works is like you write your code, in our case it's the model and the view, and you separate out the logging part. Now there is an expression which will tell the compiler of AOP that this logging has to go into these particular functions. So this expression is called point-cut. So just remember these three things, these would be used later. So you have cross-cutting concerns which is the intent in our case it's logging. Then you have an advice which is the code which is inside that logger. Then you have point-cut which is an expression which lets you know where to put this advice. The whole thing is called weaving. So this is what I was talking about. On your left side you have your classes, your code, your logic. On the right side you have the cross-cutting concerns. So we have modularized the cross-cutting concerns and these get pushed inside your code based on the point-cut, which is the expression which tells the Aspect-J library wherein this code should get injected. Fine, so let's move further. So this is how Aspect-J works. This foo was your function. This foo gets converted into foo-o, which is foo-original. Then there is another counterpart function called foo which gets created. So throughout your code you must be calling the function foo. Instead of your function now the new function will get called and inside the new function you may choose when to call your original function. We will get to know more about this when we see the code. So what we are trying to build over here is we take the point-cut, we take the advice. All these come from Aspect-J. Now we will try to control this point-cut and advice through configuration from the server. So let's go back to the example that I gave. You are trying to log the performance. You don't want to log the performance all the time. What you want is you want to log the performance once, get the values and then switch it off. So we will do the same thing. We will have a point-cut which says inject the code of logging everywhere. Of course the code of logging is your advice and we will control that code through configurations. So this is what we are trying to do. We are trying to take control of your function before it gets executed and trying to take control of it before the execution finishes. We will create a diff. We will get a diff of the timing and print it out which will give you the performance of the function. So this concept can be used for multiple things. Debugging issues which I told you. We will do it through logging. Arrest crashes. We will try to fix logical issues as well using the same concept. You can even disable a function using it. I talked about advice. So in case of aspect j, aspect j has three kinds of advices. An advice can run before a function call, an advice can run after a function call or an advice can run instead of a function call. Which means is if you have an advice which is called around advice that will run instead of your function foo. We will be using around advice. So if you see this advice is running around your function foo o which was the original function. So before you call your original function we will first log the input parameters then call your function and then log the return value. So this logging can be used to diagnose issues. I will tell you how. Say you press a button, something happens on your screen and then the whole rendering, as the whole rendering stops in between this rendering you figured out that there is some problem somewhere and you have no clue how to reproduce it. So on the press of a button you can start the logging. In our case what we are doing is we are logging the input parameters and output parameters of all the functions. So this is what we are trying to do. So when you switch on this kind of logging this will give you this state of each method and the flow it took. The flow it took from the press of a button till the rendering stops. Essentially the functions that you have most of the time these functions are stateless. If you log the input parameters and log the return value there is a huge chance that you will be able to figure out which function started behaving badly. We will use something similar to put in a try catch around the function. If you remember as I said we are using around advice so you are free to call your original method as and when you need and we can also choose not to call it for switching off the call completely. So this is how the final advice will look like. You have config coming from the server then you have the big advice which has one portion of code which does logging for you. There is another portion of code which has a try catch then there is a portion of code which disables a function and all these portions are controlled by configs which are coming from the server. This could be a sample configuration which you can think of you have given. So this essentially has to come from the server and it will have the name of the class the name of the method, action and the data. So let me go to the code, piece of code that we have written. So if you essentially see the first line is the point cut. Let me go deeper into this. It's the expression as I said this will let you control which which function on which function should this advice run. So in this expression, if you see there is execution and then there's a star. This star tells you whether you want to apply this point cut for public or private. The next is the package name and the star after that tells you all the classes inside the package should be weaved and then the other dots inside the parenthesis which say that any kind of parameter is acceptable. Then on the fourth line, you can see the around. This is the advice that I was talking about. This is the function which aspect gives you. You can write this function and the control will fall inside this function. So if you see this, there is a class called preceding joint point. So aspect J while running this joint point will give you all the data inside this object from this object. You can figure out on which function this point cut this advice has got applied. So if you see, I'm trying to get the name of the class, the name of the method. And then towards the end, these snippets tell you how to these snippets are the examples of how would you call the real function. I told you about the function called for original in the case of aspect J that get that that function gets called through joint point or proceed. So once I figured out what's the name of the class and what's the name of the function, I'll write my code, which will, which would be the last two snippets. They will go inside and the code will work based on the action that I have specified in the configuration. So if I go back to the configuration, I'll get through the configuration. I'll pass on the name of the class and the name of the function and of course the action. So if you see here, the action would be logging action would be try catch action could be disable the function. One of the other actions, which I'll be talking about today would be change in input parameters and change in return value. Why do we need this? So I promised you in the beginning that we'll also be fixing logical issues. So crashes can be handled, but most of the times the issues inside your code are because of the bad input and output parameters. So functions ideally have to be stateless and the inputs which you get inside the function is what defines what will how the function will run. In a lot of cases, if you just fix the input to that function, your issue will get fixed. So we'll try to do that as well. So let me give you a demo. Okay. So this is the screen of my handset. I'll just bring it towards the right. Fine. So I've taken an open source code of an app called open Sudoku. It's a normal app. It's available over GitHub. What I've tried to do, I've introduced a few crashes inside this code. I'll start playing. Oops, it crashed. Let me play it a little longer. No, it's crashed. So it's randomly crashing. There's no way for me to figure out what went wrong. I'll show you the stack trace which gets printed. Let me play it again so that you can see the graph. Okay. So the stack trace gives me the name of the function. The name of the function, but even if you go to the name of the function, there could be certain scenarios in which you'll not be able to figure out what's going wrong. It would be really good if I could have added some logs, but then your app is running. And of course you won't be leaving logs in the production app. So it would be really, it, it would have been really helpful. But of course I didn't write the logs. I tell you how to write logs in the live app. So I've written a simple logger. The logger should be logging the issue and sending it to you over your server. Just for simplicity, I'm not sending it to any server. I'll just be printing those logs on my console. These are mind you have already told this log is something which has, which was never there. And I'll be logging this whole thing from the config coming from the server. So this is just a file which I'll be pulling from which I've put on my draw box. I'll be pulling it from there. Let's go to the console. Let me exit the full screen mode. So now you can see both the things. What I'll do is I'll just give the app. I'm fetching the data as in when I restart the app. Cool. If you see, you have got the data. Now let me start playing. So if you guys notice, there are certain, certain logs which are getting printed now. So I'm not built the app. The app has already always been there. This is a live app. And if you see, I press here, oops, it crashed. So these logs can help me figure out what went wrong. Oh, sorry. I kind of removed them again. So essentially what happened is the way I'm printing these logs, see this is the log which got printed when the app crashed. So through the logs, I can figure out that there is a value called four. If I press on something, if I press on the box, which has a value called four, or if I'm putting something, sorry, if I'm putting a value of four inside any of the boxes, it will crash. So these are the logs which were never there. I printed those logs on the live app through this tool. Fine. You have the logs. You have kind of figured out what the issue is. Now how do you fix it? So they could be certain scenarios in which just by adding a tri-catch, your app will start working. In this case, I was trying to put an Easter egg inside my Sudoku app. So Easter egg is something which is a goodie kind of thing inside any game, which will spring up something. I was trying to put a special feature which I don't need. It was a goodie. But if it doesn't work, it's fine for me as long as my original functionality works. So what I'll do is I'll just apply a tri-catch. Let the line beyond the line which is crashing not run. So I'm fine with that. So I'll just apply a tri-catch and I'll show you how this gets fixed. So again, this is my configuration. If you see the first line is not visible. So it's visible from back there. It's not. Okay. So I'll read it out. So the first line gives you the name of the class. The second line is the name of the function. The third line is the action. So the first piece of code which I wrote earlier was for the logging. The action is log over there. In the second case, the first line is the name of the class. Second line is the name of the function. Since it's not visible, the name of the function in the second case is called Easter Egg. This was a function which is crashing. I could have shown you the stack trace, but yeah. So this is something which I figured out from the stack trace. There's a function called Easter Egg which is crashing and I'll be applying a tri-catch over it. Cool. So I'll just restart my app. Let me kill it. I forgot to save. Cool. I've got... I'm printing the logs for the result which I'm getting from the server. This says that I've got a patch which is of the type tri-catch. This is the function on which it has to get applied. Let's play again. This time I'll directly start playing with four. See, it's working. The patch got applied. Now it's not crashing at four. So what it means is like we have fixed the most dreaded enemy that a developer has. It's the crash. The app is not crashing now. The users would be happy, you would be happy. So this was the first thing, logging. Second thing is the crash. Now let's do a bit... Let's take it a bit forward. I'll show you how to fix the logical issue. So if you have played the game of Sudoku, what it says is you are not allowed to enter numbers of the same type as in a smaller box or in the same row. So if you see on the left, on the left bottom, there are four has been entered thrice. So the developer forgot to give a different color to it. Ideally if you're playing or if you're playing in an easy mode, you should get notified that this four will not work at all. This is a logical issue that you've put in. So there's a function inside my code that gives me the color. There's a function called get invalid cell color. I'll show you in the code as well. So this function, if you see, is returning a color called white. It should ideally be returning a color called red. If it does that, we are cool. So through this patch, I'll show you how you can change the return value of a function in a live app at runtime. So again, this is my patch. The first line for those who couldn't see it. Of course, it's not visible from back there. So the first line is the name of the class, which is all dot more dot open Sudoku, GUI dot Sudoku bold view in the function function is get invalid cell color. The action is set return value. And of course, the data that you have to pass is red. I'll show you how it works. Let me restart the app. The color has changed. Fine. So we have done three things. We have done the logging. We have fixed a crash. We have a logical issue. Any customers would be happy. Now let me show you the code which I wrote for all this. The game code never got modified all this while. This is the class called trace aspect. This is the function which I was talking about. We've joined point. There's a library called aspect J that you have to put in inside your gradle. And whenever it gets compiled, it looks for this kind of this. It looks for a class which has the point cut and the weaving. As I showed you, this is the object will join point which comes in. In the advice through this joint point, you can get the name of the class. You can get the name of the function. This is the action. These are the actions that I was talking about. So what I've done is when I make a network call, I get the configuration. I get a load the configuration in a hash map called class method map through this hash map. I get all the data. And when the advice is being run, I just check whether this function needs any advice to be done. If there is no advice, I don't do anything. If there is an advice, I run it. These are the advices that I have. It's called log. If the first advice is log, which we did in the beginning. If the advice is log, I start a stopwatch. I end the stopwatch and do the logging. If the action is set return value, I run the actual function. See, this is how the actual function is run in aspect 8. It's called joint point. I let it run. I throw off the return value of the original function and I use the return value which I've got from the server. Similarly, what you can do is I have written the code for try and catch. Then you can call. There's an action called call self. There could be an action which says do nothing. So if I don't get any of these actions, I don't do anything. In that case, you just have to tell me what a return value should be because someone else who's calling this function is expecting a return value out of it. So if there is no action, the function will not be called at all beyond that is pretty simple piece of code which just makes a network call and some classes for logging. Cool. So this is what the demo was about. So we have improved this a lot in Hansel. We have kind of made it a lot faster with a lot of features. We let you change a lot of things which I can't talk from me. Now we can take it after the discussion. So this code is present in the GitHub repo have kind of made it available. Please go through the code, use the code which I have written or go to Hansel.io register yourself for a better tool. Thank you. When you showed that we are planning a solution to a problem, won't that be a temporary solution for us because the actual code is not getting changed, right? Yeah, so this code will run every time your original code runs. So it's not a temporary solution. All this, all that has gone to the app, you can use some kind of caching or persistence to it and save it. And as in when the app loads, the whole thing will get applied again. No, my question is when you are changing the color of the test, okay, in the code, it is written as white. Yes. By applying some patch, you are changing it to red. Yes. Okay, but that is not permanently getting changed. Yes. Okay, so the developer needs to, that is a temporary solution. That's what I'm saying. The developer needs to go to the code back and then they handle the condition, right? So he has forgot to mention that if there are multiple numbers present in the same row, then the developer needs to set that color back to red. Okay, that condition is missed by developer. Okay, so this developer has to do this as in when you have your next release. So you don't have to do anything for this release from the next release. If you want, you can, you should ideally be setting your code, right? So that whenever the next thing gets shipped, you don't need to apply a hard match. But for this release, you're all set. Whatever you have will always run as red from now on. Yes, but that can be a risk for like production deployment because if in case this is the temporary solution, which we have applied to currently, and if developer forgets to include that in the code. Okay. And if again the client might face the same issue. So yeah, if you forget, that's what the solution is for. You have already forgotten it once. You should ideally not be forgetting it the next time. But if you forget again, this tool is there for you. You can apply another patch for this. Hi. So aspect j uses reflection, right? I saw some code when where it was dealing with class names and types. Oh, this code was this code was to get the name of the class and names of function. Okay. So aspect j does a lot of things. If you go through the code of aspect j, it hardcodes the name of the class over there. So the reflection reflection is not used to get the name of the class. Okay. So my main concern is since we are fetching configuration and that configuration could contain any possible class in my code. So basically we are the will have to inject aspect in every class of my code. Yes. So that we can work with this configuration. Yes. So won't that affect some performance? Yeah, of course. So the way I've told that we have shown the performance impact is around five milliseconds for 100 calls, which is not a bad thing for a normal app. But if you have an extensive app, it changes a lot. I've been wearing the UI changes a lot. Then of course you'll start seeing the performance drag. I'm using a really. So this whole demo was on a device called Redmi. It's not a high end device. And I don't think you could have seen any change in the performance. Okay. This was really fluid. But yes, come to Hansel. It has. So we have improved the performance of aspect j every in our own library. We have made it 100 times better than aspect j. Is your product pro guard ready? Yes, it is. It is pro guard ready. Can you explain with some situation? I'm not allowed to speak about it here. We'll take it offline. Can we inject any Java codes based on the JSON file? You can only enable or disable your features. You can. You can. But can we inject some Java code? Yes. So there are two things to it. When you say code injection, it's not allowed. Android doesn't allow you. But then as long as this code already exists, it's all out. So if you are somehow sending a dot, sorry, a compiled code from the server, Android will ban you. If you are sending something which already exists, you're good to go. Thank you for the awesome session. I want you to ask, you are there, right? So you have already inserted some code. Yes. And also, so first you have changed the code. As you are saying that you cannot insert the code, but you have inserted tri-cat. Second thing, if you have inserted a tri-cat, the line number would change. And again, if I get a crash on some next line or somewhere, the line number will be mismatched and the crash log that I see, that won't be for that line. Not exactly. So what happens is, I'll show you the line the way it works. And I'll show you directly in the code. So if you see this, joinpoint.proceed, this actually calls your function. So when the crash happens, the crash will still give you the final line. Though you see this class and this function inside somewhere in the stack place, but the final crash will happen in your actual function. So the stack place will definitely lead you to the final value, final function. Hey, I'm kind of a beginner in terms of Android. So to basically put, I mean, as far as, like, this hotfix and kind of things, like, I'm just trying to understand, just correct me if I'm wrong. Is it something like you have some Boolean values for every step and you send those configuration files from the server and if you want to stop something, you send faults for the particular step and that won't get executed. Is it something like that? Yeah, this is, in the example, yes. This was exactly the thing that we showed, yes. So you have a lot of advices already written. Now you switch them on or off based on what you're getting from the server. What you're getting from the server is the name of the class and the name of the function and the action. If the action says open it up, you open only that piece of code. Thank you. But then there is a lot which you can do. This is a really small sample. Thank you guys.