 Good morning, everyone. Welcome to the presentation. The title of the talk is Bootcamp to Appium Android Internals. So before we go down to the session, let me take a quick moment to introduce myself. I'm Shravan Medharapu. I work with Badoo as a software engineer at QA. Badoo is a mobile-lating app company. We also have a product in India called Bumble. That's about me, what I do and where I work. Apart from that, I was also a core contributor to Appium in Appium, you automate a two-driver and you automate two server modules. Before getting on to the presentation, let me quickly understand how many of you, at least, written or executed a single script in Appium? Yeah, fair enough. Thank you. So if you're someone who used Appium for quite some time but don't know much about the internal stuff, what happens inside, when you execute a fine command, what happens inside, if you're someone who used Appium and face some issue with the Appium API but don't know how to debug this stuff. Or if you're someone who are using Appium but want to give your own implementation by extending or implementing your new Huanla in Appium, this session should help you in doing so. So let's get started. So we'll be discussing that presentation in two parts. In the part one, we'll be discussing more about the Appium basics. And in the part two, we'll be specifically looking into how it works in terms of your automatic driver and your automatic browser. So if you pay the right attention, by the end of the presentation, you should get a hint how to implement a new Huanla and extend the entire Appium code base. Make sense? Let's get started. How Appium works? On a very, very high level, on Appium, you have three layers in it. This diagram, which are here, is from the context of Appium on right. We have three layers. One is the client model, where you make a request to perform an action. And we have a couple of servers. One is in server running on host mission, which we generally call it as Appium server. And you also have another server running on your phone. So here the client model is where you make the request. We have different client bindings available. Let's say Java bindings, C-Shop, Python, Ruby, and so on. But if you're using Java and want to find an element, this is how you typically make a request to find the element. It's a driver.find element by ID with some whatever ID that you wanted. When you give this request, what this client model does is it interprets the given request into an HTTP request. So when you make a request to find an element, it turns out to be HTTP request. Here is the HTTP request for the find element. This is the post method, again as some specific API, but also you have got some payload. In the payload, you are conveying about your search criteria. You are trying to find element by using ID with some value of ID. So when you make this request from the client, we are hitting the server running on your host mission, which is an Appium server. And the Appium server consumes the request, and what it does is it tries to find out who is responsible for the given implementation. If the server itself is responsible for implementing this find element, it will go and execute the implementation. But if not, what happens is it forwards the request to the server who is responsible for the implementation. Let's assume that the find element implementation is not responsible by the Appium server, but other server that is running on your phone. So in that context, we are forwarding the same request to the server running on your phone, and the server running on the phone consumes that request and acts on the request. And whatever the action response that we have, it sends back to the client. So let's assume that the request is failed because the given element is not identifiable in the screen or app. So in that context, you will get a find and status code, and you also get some JSON response about the operation. What's interesting here is the JSON response that we are getting. With the help of JSON response, the client actually tries to understand what the response is. Here the status you see in the JSON response is 7. What does it mean by 7? The 7 has a unique definition that is element not found. We're not sending the entire information, we're just sending the status code. With that, the client is in sync with the status code, so it will try to decrypt what it is. And we are also seeing some string representation of the entire execution which is put in the form of value. And finally, we also have a session ID. Why do we have a session ID here? It's to distinguish the from one session to another session. So we are sending some status to the caller. The caller here is the Appian server that is running on a host mission. And the server running on the host mission takes that response and forwards the same to the server to the client. And finally, client receives this request and interprets the given response. And what response that we have is a 500 status code, and we also have a status as 7. And it interprets and finally comes up with the final outcome of the entire execution. So the entire execution result is no such element found exception. That's how the client module works. So if you summarize the entire stuff, what these individual modules are doing, the client module is trying to interpret the given request. And whenever you get the response, it again interprets the response. That's what the client module is doing. And the rest of the two modules we have, the Appian server and the server running on the phone, are there to serve your request. Based on the type of action, it might be served at the Appian server or it might be served at the phone server running on the phone. So that's what happens during this execution flow. So let's move on. What happens during the Appian server starting? This is the key thing that we need to understand. What happens when you start the Appian server? If you're starting the Appian server from the GUI, that is Appian GUI that you have, or from the command line, these are the general logs that you see, isn't it? So what the interesting thing is, we need to understand what happens when this piece of logs get executed. Inside, specifically, if you're running the Appian from the command line, the command that you use is Appian. But how does it know what you should do when you run the Appian command? You have that mapping available to these.json of Appian module. Inside the bin object, you have an Appian command here, and it's again mapped again as some file. What the file name here is main.js that is available in Appian module we have. So now we need to look into what's going on in the main.js file. Let's quickly understand what's going on inside this class. We have a main function which is being exported. And if you try to understand the code flow, say the code flow that you're saying is not exactly same as that we have here in the Appian code, but we expected the core information that we need to understand. The first thing that it's doing is instantiating the Appian driver. What is an Appian driver here? The Appian driver is responsible for managing your driver and also manages your session details. And we are doing, we're looking at another function called route configuration function. What does this route configuration function does? We need to register the routes that Appian supports. What do you mean by routes here? It's the API that the Appian supports. So when you're trying to find an element, it's turning out as an HTTP request. So it's an API. So that list will be registered over here, but how does it know what are the supporting libraries that we have? That information is being fetched from here. The information that you are seeing here is from the routes.js of Appian base driver. And these are all the possible routes that we have for an example. We just extracted a couple of routes that we have. The first route that you are seeing is route for the find element. So if you carefully observe, we have a key-pay value information here. The key here is an API part. And for information, this is the API part that we have for the find element. And in the value, we have a bunch of other information. It is a specification for the entire API part. And this, the post method, talks about the HTTP method that we have. And what's interesting here is the command. What the command conveys to us is we have a find element API, but we don't know where it's been implemented. That implementation details we'll get from here. Here the command find element will tell us that we have the implementation available in this function. But where we have this implementation function available is something that we're going to see in some part. But for now, remember that we have the implementation available in this command. And finally, we also have some payload information. That is not something that we want to have a look at right now. So if you summarize this entire stuff, we have an API part, and we also have HTTP method, and where it's been implemented, and we have some payload. And this one that you are seeing here is a route that is related to hide keyword. We pretty much have the same information here. API part, HTTP method, and finally some payload. So when the server registers this stuff, server knows that we have all these possible API parts, and server also knows where it's been implemented. So when you make a request to server that they want to execute a find element, it just goes and executes this find element function that we have in the API. But where do we have this implementation is something that we're going to see in some time. Makes sense? So that's what the route configuration function does. We're invoking another function called base server. What does this base server does is it will start the API server and register the route that we just see. With that, we have API server and up front running. That's the flow. When you start the API server, that's what happens in the backend. And the next one is the important modules related to Android. So we are in this presentation, we're only talking about the Android, the iOS, and the rest of the topics. We'll only be seeing what are the important modules that we have in the Android context. The base server, Appian base driver, is the base for the all Android drivers that we have. It could be an iOS driver, Android driver, or Windows driver, whatever the driver it is. It's all backed by Appian base driver. But it also got some submodels in it. Let's have a look at what the submodels and what they are responsible for. We have a base driver which is responsible for ordering the driver and the session details. And you also have a express. Express model is a Node.js model, again, which is responsible for managing your Appian server. When you have a server up front running, it's the model that actually does the entire thing. And jsonwp proxy and jsonwp status are responsible for managing, proxying the request to the server running on a phone and interpreting the status that we got from the server. That is about these two modules. We also have a protocol module. What is this protocol module does is it actually register the routes that we have. The couple of routes that we have seen is actually lying inside this protocol module. And all these submodels are available in Appian base driver. That's about the base driver. But we also have some implementations for this driver. One of the first drivers that we have in Appian is Appian on-ride driver, which is backed by Google Appian, Google UI Automator 1 API. And we also have a Serendroid driver, which is there to support Serendroid for you, but it's no longer being used or maintained. Now it's been deprecated. The actively maintained driver that we have right now is UI Automator 2 driver, and which is backed by Google UI Automator 2 API. It actually supports the latest versions of the device for you. And we also have a very recent driver being implemented called sPressDriver. This sPressDriver is backed by Google sPress of Test Automation framework, and we'll not be discussing more about this sPressDriver, but if you are interested how it works inside what happens during the entire execution, I highly recommend you to join my colleague, Rajthi, presentation that is going to happen in the same room at 4.30 p.m. And by the way, Rajthi is one of the contributors of Appian sPressDriver. But for the rest of the conversation, we'll be looking into the UI Automator 2 driver, how it works, what happens inside and everything. That's about the few driver implementations, but we also have some utility sort of Node.js models related to on-ride. One of the first regularly used modules is Appian ADB. Why do we have this module? It's actually wrapped around ADB that we have. If you remember, we are using ADB commands for quite many reasons. Let's try to understand where we're trying to use them. We are using this Appian ADB module to find the connected devices. When does this happen? It is during the create session flow. When you try to create a session, there's one of the few logs that you see. What it is doing is trying to find the connected devices in the host machine. At every line of the log, you actually see a module name here. At the first line, the Android driver is the module name, and the ADB is also a module name. If you are trying to analyze any logs, you know where it's been generated by looking at the module name that we have. That's about finding the connected devices. We are also using this module during installing and installing the APKs. When does that happen? Again, during the create session flow. We are also using this APM ADB module to fetch the device details. What sort of device details? It could be a device API version, or it could be a screen size information for that sort of details. We are again using this module, and we are also using this module for doing the port forwarding. Basically, why do we need to do the port forwarding? It's something that we're going to see sometime. But for now, remember, by using this APM ADB for doing the port forwarding. We are using it for quite many other places, but just remember, wherever you do the ADB commands, it's from the APM ADB. We also have an APM Chrome driver. What it does is it actually manages Google Chrome driver. Why do we have a Google Chrome driver in place again? We are using Google Chrome driver to interact with the web views in the hybrid apps or to interact with the web application. Let's say Chrome application in the Android context. This APM Chrome driver actually does that stuff. We also have an utility module called APM support that is there to help you in doing file or image related operations. That is about the utility modules that we have in APM. With that, we pretty much cover the part one of the module, where we discuss the basics of APM, how it works, and what we've got in the APM. But now let's look into the APM view automated driver and view automated server. The first thing, where do we have this APM view automated driver and APM view automated server comes into the picture. The APM view automated driver sits in the APM server as a sub module, but APM view automated server is an independent module that actually runs inside your device. Makes sense? Driver module sits in the APM server and view automated server sits in the device by you. Let's try to understand what these modules are responsible for. The key responsible is basically. The view automated driver is responsible for managing your session, and when you say session, it is responsible for giving implementation for the create session, delete session, and it also responsible for starting the view automated server in the device. What it does is it also gives the implementation for the handlers. If you generalize the APM handlers, there are two types of handlers. One is a handler that's been implemented at the APM level itself by using APM ADB or sort of things. Let's say you want to install an APK, or fit some device details, or have the keyboard, those sort of things can be achieved by implementing the view automated driver itself. The other category is you want to relay on some other modules. Let's say you are automatic server or Chrome driver. It just proxies the request to the corresponding module. So if it is something, if it is an API that has been implemented at the automatic server layer, it just proxies the request to the automatic server. But if it is something that's been implemented at the Chrome driver, it just propagates the request to Chrome driver. And we are only using Chrome driver for WebView related options. Makes sense? That is about the view automated to driver. Let's move on to the view automated server. A view automated server is responsible for giving a lightweight server. It manages the lightweight server for you, and it also gives the implementation for the handlers that you have. Handlers can be a find element or clicking operations. It could be anything. All those implementation goes in this one. So that's about these two modules. If you summarize these two things, the driver module is responsible for managing the entire session and proxying the request to the corresponding module. And whereas the server module is responsible for managing a lightweight server in the device and give the implementation for the handlers that it's supposed to be. That's about these two modules. But now we are going to see an interesting part. What happens during the create session? If you're using Java Client, the typical way how you make a request to create the session is by using these capabilities. What information is important here is the automation name that we are specifying. The automation name capability that we're explicitly setting is the UI Automator 2. What is UI Automator 2 here is we're explicitly requesting an RPM server that we want to create a session using UI Automator 2 driver. And we also understand just before, when you make a request from the client module, it actually interprets into a HTTP request. So the create new driver is something will turn out like this. You have a post method, again as some API path. You also have a payload information where you have desired capabilities. The desired capabilities or the payload is something that we don't bother about it. But what is important here is the API path and the HTTP method. Why do we need to bother about them? Because that's what the server actually respects. It doesn't respect much about the desired capabilities, whether you have right property or something to give, actually, at least to work on it. And we also see we have these routes available which gets registered during the RPM server start. This is the route that is related to create session. You have an API path, again as a post request, and you also have a command that is called create session. So it must give you a hint why we have a command create session here. The RPM server will invoke this create session function when you request for creating the session. How does it happen? It's using this function. You don't need to bother about the code base here, but you need to understand what the essence is doing. The key thing is we have a spec command here. This spec command will actually map it to routes that we have and the route command that we have. The route command that we have is create session. So this spec.command will turn out a create session. And what does this execute command does is it invokes the create session for you. It actually invokes the create session function. But where does this create session function exist? It's something that we want to look at. The create session function should be available at every model that we have, every driver model that we have, whether it could be an E-automated driver, or S-plus-so driver, or a salandoid driver, or apm-automated driver. But we explicitly specified in the capabilities that we want to make a request by using the E-automated driver. So this create session function will get invoked inside the E-automated driver that we have. Here is the create session function we have inside E-automated driver module. We're not putting it into the code base, but we're actually looking at the essence of what it is doing. The very first thing that it does is it actually creates the session ID and register the capability. That's fine. But why do we need to create session again? Why do we need to have a session ID? Session ID is needed to support the parallel execution. Everybody knows that apm actually supports parallel execution. So how does it know from one session to another session? It actually takes the help of session ID to distinguish from one session ID to another session ID so that it will actually distinguish from one session and another session actually executes in the right device that we want to do. So that's about why we have a session ID and why we are registering the capabilities and moving on, it actually installs two APKs in the device. Namely, you are automated to server APK and you are automated to server test APK. One is server APK and the one is server test APK. Why we have two APKs is the first server APK will have a server implementation and the rest of the test APK will have access to the server and it just starts the server for you. So server APK has the implementation and the test APK will just start the server for you. That's about it, but how does it happen? How does the server start? It is by using this command. You don't need to understand the entire thing but you need to understand a couple of arguments that were possible. Basically, it actually starts in a test for you that is available in the test APK. The first parameter that we are seeing is the package name of test APK. So the second parameter that we have is the instrumentation test terminal. What's the beauty of the instrumentation test is it actually has the access to the server APK that we have. We have two APKs. One is server APK and one is test APK and we are running the instrumentation test from the test APK. So the test APK will have access to the server APK for this and it actually starts the server for you. With that, you have a server up and running. That's fine. You have a server up and running and you are in host mission but how do you make a connection between these two modules? We need to make a connection so that we can give some request to perform an action. To do that, we have to do the port forwarding. That's the reason why we have ADB port forwarding. To do the ADB port forwarding, we need to execute this command with the help of the APM ADB module. What is important here is the couple of parameters that we are passing. The first port number that A2W0 is the port that we have in the host mission. And the second port 4723 is the port that we have in the server that is in the device. When you execute this piece of command and make a request from the host mission, it actually forwards the request to the server running in the port. So how do you make a request to execute an API in the server in the device is by making a HTTP request. And how are we doing it is with the help of ADB port forwarding. That's about ADB port forwarding. And finally, what it actually does is it actually installs the application on the test for you. So you are actually passing the application on the test during the capabilities. It takes the help of the capabilities, installs the APK for you and launches the app. That's the overall flow of create session. But if you summarize, what we got with the help of Uautomotor Uautomotor create session is this. The Uautomotor server driver module, which is sitting in the APM server, is starting the server in the device and doing the port forwarding to make a request and also starting the application under test. With that, we have the right platform to execute the test case files. We are running application under test is launched and port is being forwarded. With that, we have the right platform in place. That's about what happens during the create session. Now let's try to understand these internals, what we got in these two modules. First, let's have a look at the driver module. We pretty much discussed everything about the driver module on the core sense. To summarize, we have implementation for the create session and it also gives the implementation for the commands that we have seen in the routes. But what is important is the server module. It got a whole bunch of stuff. To summarize, we have two APKs as we discussed. One is the server APK and the other one is server test APK. The server APK is responsible for managing a server and it also responsible for implementing the APIs. Let's say a find element or making a click action, those sort of things will get actually implemented at this final module that we have. And the other APK is responsible for having a single test case. And what does that test case does is, it actually starts the server that is available in the server APK. That is the only way that we can actually start the server. Otherwise, you're sitting on a host mission, you have a server running off, you have a server APK in the device, but how do you make a request to start the server on its own? Making the help of test APK to start the server on its own. If you outline the entire architecture, this is what we got. We have both APKs running in single process, but if you get into the first APK that we have, which is a server APK, you have a native server, which is a lightweight server, to respond to the request that we have. We have a request handler where you have the implementation for the APIs. Again, find element lakes, whatever the stuff that you have, all this implementation will be implemented at this point. But how does the server knows what implementation you have? We need to register in this server again. That happening in the APM server. So what APM server is doing is, it actually registering the implementation functions that we have in the server. But if you want to have a look at what's going on in the APM server, it's just a class that is extended by some server. And what it does is, it register the handler based on the HTTP type. First thing is, it is registering the post handlers for you. And if you get into this one, it is registering the... It instantiates the find element implementation by passing the right API path, the implementation class number, and HTTP type. With that, you are registering the implementation of the class for the API in the server. And when it's our request, it goes and executes this class that we have. Make sense? Moving on. We have another APK call test, APK. What it does is, it actually has a single test case, single test to start the server in the device. If you get into the server test that we have, it actually does two things. First thing is, it starts the server and waits until the server stops. So it waits until the server stops, it's fine. But how does it know what's the happy part for the server to stop? It actually happens when you make a request to delete the session. That's when the server knows to start or shut down the server. And that's when the test case actually ends its life cycle. So the server tests that we have, starts the server, and waits for the session to be deleted. That's about the internals of this one. But the thing, how does these two models communicate with each other? Again, this is the outline of this model, how one model communicates with each other. You have a U-autometer driver where you make the request to server, and the server running on the phone consumes that request, takes the help of AppM-Sablet, invokes the corresponding implementation, and that implementation class takes the help of Google U-Autometer API to interact with the app and do the right action. And the same Google U-Autometer API gives the response about the entire action and sends this entire execution status and which actually sends back to the colleague who is actually U-Autometer driver here. The same status again sends back to the client. That's how these two models communicate with each other. Now, moving on to the last topic of the presentation, which is a very interesting one, how to give the implementation for the new handler. If you want to give the implementation for the new handler, you have to make the changes at the appropriate pace. On a very, very high level, we have three models. One is the client module where you make the request. We have a driver module sitting on AppM server, AppM host mission server, and you also have another server running in device. So to give the implementation for the handler, we have to make the changes across all these layers. But the client model is something that we have different bindings available, and the implementation might differ from one implementation to other implementation. We'll not be looking at the client, but we'll be looking into the rest of the two things. We'll be looking into the new automative driver implementation and the new automative server implementation. The implementation for the new automative driver is pretty much similar with the other drivers we have. So if you know how to give the implementation for one driver, it's pretty much the same for espresso driver, serendroid driver, whatever the driver it is. The first thing is we need to figure out what we wanted to do. Let's see what we want to do here. We want to achieve this functionality. The first thing we want to do is we are opening the quick settings in the device. That's the first thing that we want to do. We want to define what we want to achieve. We want to open the quick settings. And let's see where we have to make the changes. And the next thing is we have to define the specification for the API. I wanted this entire operation to be a post call, again as the quick setting API that the API part that we have. And the other thing is where I want to give the implementation for this function. Why do we need to know about this one? Because I need to tell this server, Appian server, that I have this implementation for this API part in this function name. And the next thing is the registering the route. As we discussed, when the Appian server starts, it actually registers the routes for you. And this is the route that we want to register in the server. So server actually knows where we have the implementation. Makes sense. So the important thing is the command name that we have. The command name that we are giving is the open quick setting. And that is the same function that we want to give the implementation. But where do we need to give the implementation for this function? We want to give the implementation of your automotive driver for now. But it is the same for across all the other drivers. It could be a Serendroid driver, Espresso driver, or Appian Android driver. It's pretty much the same. So when I'm going on and giving the implementation in the Appian automotive driver, you could see the corresponding class name over there on the top. So I'm registering the open quick settings function in the commands and giving the implementation. Now the fancy thing here is I'm forwarding the request with server running on the phone. But if you want to give your own implementation, which can be done without the help of server module or by using the Appian ADP commands, you can go and give the implementation right here itself. But my intention is to forward the request to server running on the phone. When you do this sort of thing, it turns out like this. We already have the port forwarded and the server is running on the phone and the port that we forwarded is 8200. So it hits the server that is running on the server. That is running on the device. So it finally turns out like this. Now the thing is we want to give the same API implementation in the server module that we have. Let's see how we can give the same implementation that you are able to serve. So again, it's the same API path, same HTTP method, same function that we want to achieve. Again, we need to register our route in the Appian server. Why do we need it? We need to tell the server, I have the implementation for this function. When you get the request, go and execute this functionality. So let's see how we can register it. What we're doing here is, as it's a post request, we are registering in the post handler. If it's a get, you go and add it in the get handle that we have. And how we are registering it? By instantiating the class that we have that we are going to give the implementation and by passing the API path. Here you go with the implementation. We are creating a new class with the class name that we just registered and by extending the save request handler and giving the implementation right here. The first thing that we are doing is giving the implementation for the save handle. When the server receives the request, this is the function that gets executed. So whatever the logic that you have to execute has to be inside this function. So that's about it. And now, we are taking the help of you, Automator 2 API, and doing the whatever action that we wanted. It is very simple API to open a quick setting, which is not available in Appian right now, but we are giving the implementation right here. So when you do this one, it actually does the thing that we wanted. It actually opens the quick settings. Make sense? With that, we did what we wanted. But we will tell the client that I performed some action. I also have some response for you about the acknowledgement, basically. So finally, we are giving some acknowledgement. What information is needed here is the session ID, which actually gives you information about where it actually gets executed. And a status code, which actually conveys whether the operation is successful or not. And finally, a user understandable string representation. The entire response will turn out like HTTP response like this. The key thing here is the status ID, session ID, which actually gives you a hint, the distinguish from one session to another session. And the status that we have is 0. 0 indicates it actually got successful. And the value we have here gives you a end user understandable string message that we have. That's fine. But what if I want to send some different status? Let's say that my whatever action that I'm doing is not successful. So I want to tell the client that action is failed. That information probably you can look up to this, look up table. I think, you know, the names that we have are self-explanatory. If the element is not found, you could just send the seven status code. If you remember in the first part of the presentation where we tried to find an element, we actually got seven status codes. So that's how it is. The client model do a bit of the status codes and interprets based on the status code. We just send the status code here based on the requirement. With that, we're done with the critical part. Now let's see how we can give the implementation for the same thing that we have here. So it's basically a live demo. Let's hope for the best. What we've got here is... Sorry, let me mirror my screen. Uh-uh. What we've got here is we have a device or emulator connected. We have an APM session already created. This is the log that you see from the APM session that we already have. I'm not sure whether it is visible to the last, but why do we need to care about this? We actually wanted to choose the session ID that we have so that we can request the server to perform an action. So what I'm doing here is I'm just trying to capture the session ID that we have. So we are capturing the server session ID that we have. And before getting into the implementation, let me show you the demo for it first. And probably later, we can see how we implement this. So it's just an HTTP request. So whether you do it from the Java client or somewhere, it doesn't matter. The easy way is to do a call request or doing from the postman, whatever is easy. I find doing a call request is easy. What I'm doing is I'm trying to... Sorry, just a second, please. So what I'm doing here is I'm just making a HTTP request. Again, it's the API that we just implemented. And finally, we also have a device up and running. We already have a session in place. When you execute this API, we expect to get our open Quick Settings 4.0. So that's fine. Now we just achieved what we wanted. But what goes inside is something that we're going to have a look at. So now let's get into the code base of how we implement it. If you remember, we actually wanted to make a change at two levels. One is the U-Automated Driver, and the other one is U-Automated to Server. First, now let's look into U-Automated to Driver, what changes we made. The first thing that we made is we're given the implementation for the open Quick Settings right here. Where did we give this? We are inside the U-Automated Driver, inside the commands. We're adding in the Action Start, the actions.js, and we're registering the commands. When you register in the commands, using the server starting, it knows what the implementation, what sort of information that we have. So if you don't do this one, and just give the implementation, the server doesn't know whether you have the implementation or not. That's the first thing. And the second thing, we need to give the implementation for this API and the server model that we have. Now let's go back and see what we've got in the server module. This is the focal code base of... This is the focal code base of U-Automated to Server module. I'm not sure whether it is visible to be lost. What we're doing here is we're giving the implementation for open-click settings, which is being registered in the App-AMS applet. So if you see here, we're registering our open-click settings by giving the implementation. If you look into the implementation part, we're giving the implementation for the safe handle, and the fancy thing that we are doing is this one. We're taking the help of Google Automated API and invoking what we want. Fine. And finally, we're giving some response back. Now let's see the entire flow by debugging it. So what we're doing here is I'm trying to enable the breakpoints by attaching the debug process. And we also have enabled the breakpoints in the U-Automated to Driver module. And let's see, I'm resetting the device state. Let's try to understand the same flow again by executing the request again. So what the expectation is, it should come and stop at the Driver module first. That's what happened. It came and stopped at the Driver module. And what we're doing here is we're just forwarding the request to the Driver module. And when you do this, we already have enabled the breakpoint over there. And it just goes and stops over there. And when you execute this sort of statement, you should suppose to execute the required action that we wanted. And finally, sending the status back. And if you look at the request that we made, we still not got the response because we're still executing a function. Finally, giving the status back. And with that, we got the response that we wanted. Makes sense? That's about what we did. Just we had a quick implementation. We have this code base available in the references. Probably you can have a look at later point. That's about the demo part. So we got the references here for both the Driver module implementation and Driver module implementations. And we also have some references for the entire stuff. And by the way, entire slides have been added to the Confinition portal. You can go and download the slides over there. And probably you can have a look at the stuff. And if you have any questions, please do allow me to answer. But if you have a later point, if you have any questions after going home and looking into the stuff, you can reach out to me and share the feedback or questions at the link that we have here. Hi, Sriman. Hello. I have a question with respect to UI2. So when we, say, find element using XPath, so how does UI2 will go and look that? So what I understand from the code is it will generate an XML and it will trace back. So for this purpose, I understand it takes much longer time. So when I say find element by some heidi, let's say find element by this current exception. So then how does that work with UI2? Let me answer the first question, which is XPath stuff. I'm not sure whether you are aware of the UI Automator viewer, which actually dumps the UI and gives you a nice visual representation of the entire code. What we're doing here is we're relying on a viewer dumping the entire code base, creating an XML file, and then querying one XML file. That's about the XPath flow. And the second question about the IDE. In the context of your IDE, IDE could be a class name or whatever the context. It actually relates on the Google UI Automator API, where you have a nice way. By the way, the UI Automator API is pretty much similar to the Selenium API. What you do is you just go and query based on the IDE or by class name or whatever. It will return the IDE. So that's the difference between the XPath and the IDE. So in case 2, it will not generate an XML? No, it will not. That's the question. Why do we have 4, 7, 2, 3? Any specific reason? There's no specific reason. We just want to find out. I just got to know about it yesterday. So if you see in iPhone, there is a key for IE 7, 2, 3. It means iPad. We want to pick a random port, which is not actually matched to the other ports that we have. So 4, 7, 2, 3 is something that we could find. We found it is not generally used. And again, it's customizable. The port that we have seen is something by default. You have a capability to alter the port numbers. And one of the unknowns of 4, 7, 2, 3 is something to do with the JSON hugs, because when he was spiking out APMN, when he wanted to do something, and that was unlock code for his iPad or something. So he ended up saying, okay, it's 4, 7, 2, 3, and I'm going to put this as a port number. And that's how 4, 7, 2, 3 came into play. Any more questions? We can take one more. Okay, so actually I have three questions. Is it okay? No, it's fine. I will make it quick. So the first, I have seen the design of the architecture, how the interaction, the driver, and the UI automator from Google. And where is the place of the Google automator driver itself? Is it in the OS level or any place? It's like a dependency in the server module. We have a gradle dependency management file. We specify the dependency, which is a Google U automator tool. Okay, great. That's the first one. And the second one, I saw that you have to give the response back to the server, right? After you do the action, did the action, and is the response itself given by the Google UI automator driver? It's something that we're cooking up on our own. Okay, so the response itself is returned by our own function. That thing is based on the action that we are doing. Let's say you are doing some click action, and we are reeling on Google U Automator API. And Google U Automator, by nature, gives you some response back. And based on the response back, we are cooking our own response so that client will be aware of the same thing. And the thing is, we're just reeling on the WebDriver protocol APIs that we have, the status code that you see is pretty much similar to Selenium WebDriver. Okay, so basically, we kind of merge the response from the UI automator and our self-function. We are not merging it, basically. We are cooking our response based on the API response that Google U Automator API is. Okay, great. And the last one, actually, is it worth it if we contribute to the UI automator itself because the expressway is coming soon, I mean? Yeah, good question, basically. The thing is, this driver came about two, three years back. The expressway driver came about a couple of months back. I think from my personal opinion, the UI automator driver might get deprecated at some point or get merged into a single model. Now, we have four different driver modules. The plan is, you know, see the feasibility of how we can merge these two modules and make it a single unit module and use it across. Okay, I see. Thank you very much. Good. Let's give a huge round of applause to Shravan for coming and sharing. So I would... The best part of this talk would be... Hold on a second. Sorry. So if you have any questions or feedback, I would highly recommend you to do... access this UI and give the questions. If we don't find the time right here, probably we can discuss it later. And we have a question over there. If you have time. I think we... We want people to eat first and then come back. So that's about it. And you have one more slide, if you don't mind. Okay. And finally, we are hiring. You know, probably if you are interested, probably do check out this portal and do let us know if you are interested. That's about it. Thank you, Shravan. Thanks a lot.