Tuesday, April 26, 2016

AWS Lambda: Setting a temporary maintenance page in an Elastic Load Balancer during a CodeDeploy deployment

When using CodeDeploy to push updates or perform maintenance on an Auto Scaling Group, the default behavior is for the ASG instances to be excluded from the ELB, leaving users with a blank page. This is awful UX, and is not acceptable for a production application. It seems to me that it should be basic functionality for ELB to allow developers to define a 503 page, but that's a rant for another day. Some people have toyed around with Route53 weighted routes, but that solution is very clunky as it takes time for the DNS entries to expire before they are refreshed by clients. I've come up with a reasonable solution that uses CodeDeploy triggers and an AWS Lambda function to temporarily display a maintenance page when a deployment is in progress.

First, create a micro sized instance to host our static maintenance page. It does not matter what type of instance this is, so long as it has a web server that serves your temporary page (you probably also want to configure it using rewrite rules to ensure any requested route will serve your maintenance page).

Now we need to create a SNS topic to be used for the CodeDeploy triggers. In the SNS console, create a topic with a unique name. Then in CodeDeploy, create a trigger for your deployment group for all deployment statuses, and select the SNS topic that you just created. Now CodeDeploy will send notifications to this topic for every step of the deployment process.

The next step is to create your AWS Lambda function. Name your function something unique and choose your language. I used Python, and my code is provided below. Define your handler name. In my code example, this value would be lambda_function.process_cd_trigger. Create a new Lambda role that has EC2 permissions in order to make changes to the ELB/ASG via Lambda. Define the VPC that your ELB/ASG exist in and save your function.

Go back to the SNS console, select your topic, and subscribe your Lambda function to your SNS topic. Your ELB will now contain only your static maintenance site when CodeDeploy is in progress!

Here is my Lambda function:
import json
import boto3

asg = boto3.client('autoscaling', region_name='us-east-1')
elb = boto3.client('elb', region_name='us-east-1')
cd = boto3.client('codedeploy', region_name='us-east-1')

def show_maint_page():
    try:
        asg.detach_load_balancers(
            AutoScalingGroupName='[ASG NAME]',
            LoadBalancerNames=['[ELB NAME]']
        )
    except:
        pass
    try:
        elb.register_instances_with_load_balancer(
            LoadBalancerName='[ELB NAME]',
            Instances=[{'InstanceId': '[INSTANCE ID]'}]
        )
    except:
        pass

def hide_maint_page():
    try:
        elb.deregister_instances_from_load_balancer(
            LoadBalancerName='[ELB NAME]',
            Instances=[{'InstanceId': '[INSTANCE ID]'}]
        )
    except:
        pass
    try:
        asg.attach_load_balancers(
            AutoScalingGroupName='[ASG NAME]',
            LoadBalancerNames=['[ELB NAME]']
        )
    except:
        pass
def process_cd_trigger(event, context):
    cdMsg = json.loads(event['Records'][0]['Sns']['Message'])
    depId = cdMsg['deploymentId']
    status = cdMsg['status'].upper()
    dgName = cdMsg['deploymentGroupName'].upper()
    
    #Parse environment name from Deployment Group name
    env = dgName[:dgName.find('-')]
    
    #Make sure this is not just a deployment to a new instance
    deployment = cd.get_deployment(deploymentId = depId)
    creator = deployment['deploymentInfo']['creator'].upper()
    
    if(creator != "AUTOSCALING" and status == "CREATED"):
        show_maint_page(env)
    elif(status == "SUCCEEDED" or status == "FAILED" or status == "ABORTED"):
        hide_maint_page(env)

Tuesday, April 8, 2014

Reverse Engineering an Android Application

Motivations

I have been a happy T-Mobile customer for over 6 years now. Their service can be spotty (partly due to their use of high frequency spectrum, which results in poor building penetration) but they have one killer app: WiFi calling. Originally this was achieved with UMA, and you had to have a phone with special hardware to take advantage of it. With the rise of Android phones, however, T-Mobile released an app that could be installed on any device that would enable WiFi calling in software. The original WiFi calling app was developed by Kineto and also used UMA. At some point, T-Mobile switched their WiFi calling system over to IMS and began using an app developed by Movial. You can read up on the differences between UMA and IMS here. The biggest annoyance I have with these software-based approaches is that they no longer achieve seamless handovers between WiFi/cell networks.

In fact, any handing over from WiFi to cell networks when WiFi calling is enabled (even if it's not in use!) is excruciatingly slow and results in long periods of intermittent connectivity. This is because the WiFi calling app completely disables your cell radio when it is enabled. That might not seem like such a big deal until you're walking through a college campus and your phone is connecting/disconnecting to every single AP you walk past. I wanted to have WiFi calling enabled on my school network, but only in certain buildings (that I spend a lot of time in). Actually, I really only wanted to have WiFi calling enabled at home and at the computer science building.

I consider myself to be an Android novice. Sure, I've developed a few simple Android apps, but they're functionally simple and I'm not sure if I've really understood everything that I've "written" - there's been a lot of copying/pasting from StackOverflow. I decided that I could use these motivations to learn a little bit more about Android and cure one of my annoyances with my phone in the process. After all, reverse engineering is a great way to learn some of the intimate details of a system.

If you're only here for the end result, you can skip my boring story and check out the links to my code/app in the conclusion.

My name is Adam and I am a n00b


So where do you start when you're trying to learn more about a mystical feature that T-Mobile has baked in to your ROM? Google it. Unfortunately there isn't really much about the subject since WiFi calling kind of a niche feature. I did find this one thread on XDA, but I didn't expect it to work, and it didn't. Their approach was to disable WiFi calling using the pm command, which is the Android package manager. Not only did this seem dirty to me, it also requires root. Also, it references a Kineto package, so I knew it was the old version. So... what next?

Logcat. For the uninitiated, logcat is a tool used when developing Android apps that shows all system logs in real-time and lets you filter them down using log levels and query strings. I spent a lot of time sifting through the system logs to see what happened when I enabled/disabled my phone. I kept seeing one line that stuck out to me when I disabled WiFi calling: I/IPPhoneProxy(1329): receive intent com.movial.ACTION_TERMINATE_STACK

I knew that an intent was a message that could be broadcast in Android and then processed by different apps that are registered to receive that broadcast. I figured that the toggle button in the settings menu must issue this broadcast, which then told the WiFi calling app that it should disable itself. I decided to use ADB (Android Debug Bridge) to poke around in a shell on the device itself and see if I couldn't use am (activity manager) to broadcast this intent and disable WiFi calling via command line. I could see the WiFi calling app reacting to the broadcast and trying to disable itself... but it always immediately re-enabled. I did find one ray of hope; if WiFi calling was disabled, I was able to reliably enable it by starting the service using am startservice. The only issue with this was that the settings menu would get out of sync and still think WiFi calling was disabled.

Assembly... 


At this point I was tired of feeling like I was working with my hands tied behind my back, so I decided to try to pull the apps off the device and look at the actual code. Looking through the packages that were installed on the device (pm list packages -f), I found three that matched up with the log messages that I was seeing. I used adb pull to get the apk archives off the phone, and then used baksmali to "disassemble" the app. I found a few programs that claimed to convert smali back to Java, but none of them worked well enough so I ended up just learning to read smali. I've taken a class in assembly, and while not much of it stuck (seriously, who programs in SPARC assembly?) it was enough background for me to pick up what was going on in the smali pseudo-assembly code.

The first thing I tried searching for was the intent that I saw earlier. I found the BroadcastReceiver that handles that intent, calling a function that handles the teardown of WiFi calling. I figured there must be another function for enabling WiFi calling as well, so I spent a lot of time messing around and broadcasting various intents that I found in the code. Unfortunately I did not make much progress with any of this, and I was beginning to think I had hit a dead end.

Sometimes you need to take a step back and underthink things


I realized that I had been going about this the wrong way. There was clearly a simple way of toggling the WiFi calling state since there is a toggle button in the Android settings app. I revisited my trusty logcat dump and saw that the very first event that happened when I toggled WiFi calling was D/IPPhoneProvider(25446): ipphonesettings <- value=1 name=CELL_ONLY

Android settings are typically stored in sqlite3 databases so I searched for databases with that name. Once I found the correct database, I manually changed the entry in the table via the sqlite3 CLI. Nothing happened. I scratched my head for a few minutes before it dawned on me how inefficient it would be if Android was polling every database on the device for changes. Of course! It must only notify the change listeners if the database is modified through the Android API.

SO, I needed an app in order to effectively toggle the WiFi calling state. Which is fine, because I kind of wanted to make a Locale/Tasker plugin for this anyways.

Android Reflection

In an old blog post, I talked a little bit about using reflection to access device manufacturer-specific functions in an API. We want to do something a little different here; we want to be able to access and modify the settings in another app installed on the device developed by someone else. Looking through the smali again, I saw that there was a class that was used to get and set the status of CELL_ONLY

In Android, every application is sandboxed from one another so you have to explicitly request permission to communicate with or modify the settings of another app. Looking through the manifest file of my decompiled app, I saw that it used the permission movial.permission.IPPHONE_SETTINGS

I added this permission to my application's manifest. Now that I had the correct permissions in my app and the name of the method to be called, I was able to use reflection to execute the method and successfully get/set the state of the CELL_ONLY flag. Success! WiFi Calling was now being enabled/disabled properly.

Conclusion

Once this step was complete, the rest of my app simply implemented the Locale plug-in and Widget specifications. I did have to do a little more reverse engineering to get notifications for the state change of CELL_ONLY (to support ACTION_QUERY_CONDITION); I ended up registering a broadcast receiver for an intent that was consistently broadcast after an update to CELL_ONLY, and then used my previous method of querying the state via reflection.

After some beta testing, I found that Samsung devices use Samsung's proprietary IMS system for T-Mobile's WiFi Calling. This required me to go through the reverse engineering process yet again (and purchase a Samsung device...) to ensure my app was compatible with all of T-Mobile's WiFi Calling devices. Unfortunately the method I am currently using to enable/disable WiFi calling state on Samsung devices requires the app to be installed as a system application (which requires root). I still have yet to reverse engineer the Kineto app, which I will hopefully have the time to do in the near future.

I have released my app as open source on github and it is available to download on the play store. The app is compatible with any productivity app that makes use of the Locale API (Tasker, Locale, Llama) and also has a widget that can toggle the wifi calling state.

Thursday, March 28, 2013

Reading LTE signal strength (RSSI) in older versions of the Android SDK. Also, Java reflection.

I do a lot of work with an Android app that collects data about the connectivity of the phone. One of the main pieces of information that we need to get from the phone is the RSSI of the current wireless connection.  Unfortunately, obtaining a reading of this information is not as straightforward as you think it might be.

The way I am gathering cell signal info is as follows: I use TelephonyManager and a PhoneStateListener (with an overridden onSignalStrengthsChanged function) to listen for updates in the cell signal RSSI. The onSignalStrengthsChanged function gives you a SignalStrength object. 

Here is where it gets messy. The SignalStrength object contains many functions to find properties of the signal specific to a particular cell technology (CDMA/EDVO/GSM). You have to use TelephonyManager.getNetworkType to figure out which of these functions to use. getNetworkType returns a code corresponding to one of the enumerated network types in TelephonyManager. My app uses API level 9 since we want it to be compatible with a wide range of phones. Unfortunately, this API version came out before LTE was used in Android phones, so they were not included in the network type enumerations. In fact, LTE was not added as a network type until API level 11 (Android 3.0). In API 11, LTE is defined as 13 and eHRPD (a technology used for handoffs from EVDO to LTE) is defined as 14.

My first instinct was that we needed to have two versions of the app; one for phones with LTE and one for phones without LTE. However, when I did some more looking at the Android API, I found that even though the LTE network type was defined in API 11, there were still no signal strength methods. Nothing even reminescent of an LTE signal strength method was added until API 17, which is currently the latest API. This was obviously unacceptable, as very few phones currently use this version of Android. There had to be another way.

More snooping around on the internet told me that phone manufacturers actually added their own methods to the SignalStrength object in order to retrieve LTE RSSI data. This left me with the question of how to call a method that's not in the official Android API. The answer to this is the programming technique of reflection. Java allows us the ability to examine a class dynamically at run time. We can ask if a certain method exists, and if it does we can call it.

The problem of knowing which methods to call is a little harder. Tom at sevenplusandroid.org wrote a blog on this very topic that I found incredibly useful. He wrote an app that examined the methods available on the SignalStrength object using reflection. Then, his app was run on many devices. Sorting through his results, I noted that the three main LTE signal strength methods were called getLteRssi, getLteSignalStrength, and getLteRsrp. RSRP is different than RSSI, and you should keep that in mind when working with the data returned from that function.

Here is a snippet of how I used reflection to call these methods (you should probably handle those exceptions..).


//13 = NETWORK_TYPE_LTE
case 13:
 //the android API doesn't support LTE signal strength until 4.2... so we must use reflection to find these methods
 try {
  Method[] methods = android.telephony.SignalStrength.class.getMethods();
  for(Method mthd:methods){
   if(mthd.getName().equals("getLteRssi") || mthd.getName().equals("getLteSignalStrength") || mthd.getName().equals("getLteRsrp")){
    signalStrength = (Integer) mthd.invoke(signal, new Object[]{});
    break;
   }
  }
 } catch (SecurityException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (IllegalArgumentException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (IllegalAccessException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (InvocationTargetException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
 break;

Hope this helps someone! Reflection was completely new to me, so it wasn't an intuitive solution to this problem. I have used this to successfully get LTE RSSI readings using an app compiled with Android API 9 (2.3).

Sunday, February 17, 2013

Using an Android device as a USB GPS mouse with GPSd

I was wondering if there was an easy way to use an Android phone as a GPS mouse on a Ubuntu laptop (via USB). The method that I came up with is as follows:

1.       Steps to use ShareGPS BETA via USB:
a.       Download the Android SDK tools for linux
                                                               i.      Click “use an existing IDE” to download SDK Tools
                                                             ii.      Follow directions at http://ubuntuforums.org/showthread.php?t=1918512
                                                            iii.      After step 1, `sudo mv ~/android-sdk-linux /opt/android-sdk-linux`
                                                           iv.      Instead of step 2 do the following: `ln –s /opt/android-sdk-linux/platform-tools/adb /usr/bin/adb`
b.      Download the ShareGPS script
                                                               i.      Edit line 13 of files/adb_gps_usb to `ADB=/opt/android-linux-sdk/platform-tools/adb`
                                                             ii.      `sudo su`
                                                            iii.      `apt-get install nmap
                                                           iv.      `./install.sh`
c.       In the ShareGPS app, enter the settings menu and untick “use Bluetooth” and tick “use USB”.
                                                               i.      Consult http://sharedroid.jillybunch.com/user_linux.html if you have issues


This seems to work just fine in my limited testing. The ShareGPS app does seem to be a little buggy at times, so if your laptop supports Bluetooth, finding a more stable Bluetooth GPS Tethering app may be a better option for you.

Wednesday, January 9, 2013

Cross-platform communication using Google's Protocol Buffers Revisited

In my previous blog entry regarding cross-platform communication using Protobuf, I made some very elementary mistakes. The most glaring of which was my improper use of TCP.
Because TCP recv() allows for partial returns, we must make sure that we loop and recv() all of the message. This is TCP sockets 101 and it is kind of embarrassing that I missed it. Note: TCP send() can also have partial returns, but I have not bothered handling this, even though it is trivial.
The other mistake that I made was that I always assumed that a varint32 was 4 bytes in size. This is incorrect, since it is, in fact, a VARINT!

These two mistakes necessitated changes in the C++ sending and receiving.
First, I wrote a function to parse out the delimiting varint and to fill a buffer with the bytes specified by that varint:
/* 
   reads a varint delimited protocol buffers message from a TCP socket
   returns message in buffer, and returns number of bytes read (not including delimiter)
*/
int recvDelimProtobuf(int sock, unsigned char **buffer){
    //read the delimiting varint byte by byte
    unsigned int length=0;
    int recv_bytes=0;
    char bite;
    int received=recv(sock, &bite, 1, 0);
    if(received<0)
        return received;
    else
        recv_bytes += received;
    length = (bite & 0x7f);
    while(bite & 0x80){
        memset(&bite, 0, 1);
        received=recv(sock, &bite, 1, 0);
        if(received<0)
            return received;
        else
            recv_bytes += received;
        length|= (bite & 0x7F) << (7*(recv_bytes-1));
    }

    //receive remainder of message
    recv_bytes=0;
    *buffer=(unsigned char *)malloc(sizeof(unsigned char) * length);
    while(recv_bytes < length){
        received=recv(sock, *buffer + (sizeof(unsigned char) * recv_bytes), length-recv_bytes, 0);
        if(received<0)
            return received;
        else
            recv_bytes+=received;
    }
    return recv_bytes;
}

The rest of the code now looks like this:
    //allocate packet buffer
    unsigned char *buffer;
	int received=recvDelimProtobuf(clientSock, &buffer);

	//read varint delimited protobuf object in to buffer
	google::protobuf::io::ArrayInputStream arrayIn(buffer, received);
	google::protobuf::io::CodedInputStream codedIn(&arrayIn);
	google::protobuf::io::CodedInputStream::Limit msgLimit = codedIn.PushLimit(received);
	client.ParseFromCodedStream(&codedIn);
	codedIn.PopLimit(msgLimit);

	//purge buffer
	free(buffer);

This fixes the receiving side. To fix the sending side, we need to change the message size to actually reflect the size of the varint + message.
	int varintsize = google::protobuf::io::CodedOutputStream::VarintSize32(serverAck.ByteSize());
	int ackSize=serverAck.ByteSize()+varintsize;
	char* ackBuf=new char[ackSize];

	//write varint delimiter to buffer
	google::protobuf::io::ArrayOutputStream arrayOut(ackBuf, ackSize);
	google::protobuf::io::CodedOutputStream codedOut(&arrayOut);
	codedOut.WriteVarint32(serverAck.ByteSize());

	//write protobuf ack to buffer
	serverAck.SerializeToCodedStream(&codedOut);
	send(clientSock, ackBuf, ackSize, 0);
	delete(ackBuf);

Thanks to Johan Anderholm for helping me with this solution. As he mentions in a comment on my original blog entry, there is another elegant solution that uses boost::asio. I have not tried this but I am sure it works just as well!

Sunday, January 8, 2012

Using ioctl to gather Wifi information

For part of a project that I am working on, I needed a way to get status information about a wifi device on a linux machine. My first instinct was that I would just parse the output of the iwconfig command. However, iwconfig does not provide every piece of information that I wanted to gather, and one of my cohorts urged me not to resort to the ugliness that is parsing. Instead, he suggested that I look in to ioctl to gather the information that I needed.

I am constantly learning new things about operating systems, and I had never heard of ioctl before. It only took a quick Google search to realize how immensely powerful it is, and how well it suited my needs. Ioctl is a means of interacting with device drivers. You pass it a request code along with a pointer to memory.

Since I don't consider the automated gathering of Wifi signal strength to be that obscure of a task, I've decided to post my solution here on my blog. I'm surprised that I was unable to find it documented elsewhere.

There are a few libraries that you need to be sure to include. I believe I have read that wireless.h may exist elsewhere on other distributions, but every machine that my code is running on uses Ubuntu so I didn't look any further in to that.

//libraries necessary for wifi ioctl communication
#include <linux/wireless.h>
#include <sys/ioctl.h>

//struct to hold collected information
struct signalInfo {
    char mac[18];
    char ssid[33];
    int bitrate;
    int level;
};

For clarity, I also have a struct that I'm using to hold all of the data I collect.
Here is the function in which the actual collection occurs. The iwname parameter is the name of the wireless network interface.

int getSignalInfo(signalInfo *sigInfo, char *iwname){
    iwreq req;
    strcpy(req.ifr_name, iwname);

    iw_statistics *stats;

    //have to use a socket for ioctl
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    //make room for the iw_statistics object
    req.u.data.pointer = (iw_statistics *)malloc(sizeof(iw_statistics));
    req.u.data.length = sizeof(iw_statistics);

    //this will gather the signal strength
    if(ioctl(sockfd, SIOCGIWSTATS, &req) == -1){
        //die with error, invalid interface
        fprintf(stderr, "Invalid interface.\n");
        return(-1);
    }
    else if(((iw_statistics *)req.u.data.pointer)->qual.updated & IW_QUAL_DBM){
        //signal is measured in dBm and is valid for us to use
        sigInfo->level=((iw_statistics *)req.u.data.pointer)->qual.level - 256;
    }

    //SIOCGIWESSID for ssid
    char buffer[32];
    memset(buffer, 0, 32);
    req.u.essid.pointer = buffer;
    req.u.essid.length = 32;
    //this will gather the SSID of the connected network
    if(ioctl(sockfd, SIOCGIWESSID, &req) == -1){
        //die with error, invalid interface
        return(-1);
    }
    else{
        memcpy(&sigInfo->ssid, req.u.essid.pointer, req.u.essid.length);
        memset(&sigInfo->ssid[req.u.essid.length],0,1);
    }

    //SIOCGIWRATE for bits/sec (convert to mbit)
    int bitrate=-1;
    //this will get the bitrate of the link
    if(ioctl(sockfd, SIOCGIWRATE, &req) == -1){
        fprintf(stderr, "bitratefail");
        return(-1);
    }else{
        memcpy(&bitrate, &req.u.bitrate, sizeof(int));
        sigInfo->bitrate=bitrate/1000000;
    }


    //SIOCGIFHWADDR for mac addr
    ifreq req2;
    strcpy(req2.ifr_name, iwname);
    //this will get the mac address of the interface
    if(ioctl(sockfd, SIOCGIFHWADDR, &req2) == -1){
        fprintf(stderr, "mac error");
        return(-1);
    }
    else{
        sprintf(sigInfo->mac, "%.2X", (unsigned char)req2.ifr_hwaddr.sa_data[0]);
        for(int s=1; s<6; s++){
            sprintf(sigInfo->mac+strlen(sigInfo->mac), ":%.2X", (unsigned char)req2.ifr_hwaddr.sa_data[s]);
        }
    }
    close(sockfd);
}

This seems to work very reliably for me. Hopefully it will save some poor soul from having to parse iwconfig in the future. Happy programming!

Thursday, November 24, 2011

CSS Tips For Beginner Web Designers

First and foremost, Happy Thanksgiving! I've decided to write up a short blog of web design tips and tricks.

Recently I was asked to do some work on the website for my undergraduate research project. I have very limited experience designing things from scratch with HTML/CSS, so I decided to turn the website itself in to a learning experience. I decided I wanted to build the website from the ground up, using no templates and no fancy graphical editors like Dreamweaver. I wanted to understand exactly what made my website look the way it looked.

I also decided that I did not want to use any images. CSS3 provides some really cool attributes that let you do some neat stuff that normally would require an image.

I've done some very basic web development and ASP stuff in the past, but I've never really bothered using much CSS and considering how widely it is used today, I thought it might be important to learn.

So here are my tips.
  • Use http://w3schools.com/ as a resource. I have always avoided using this site when looking up things because (ironically) I did not like the site layout and the ads were a bit much for my likings. On closer inspection, I found that this website really had a lot to offer me. I highly recommend the CSS section.
  • Use jsFiddle as a crutch while learning HTML and CSS. jsFiddle is a very simple, yet incredible editor for making quick prototypes of websites. You get 3 editor panels, one for HTML, CSS, and Javascript. The fourth panel shows you a preview of what your website actually looks like. I can not emphasize enough how great this tool is and how much it has helped me understand how CSS and HTML work together in just a few days of "fiddling".
  • Use Google to search for websites to draw from for inspiration. I do not have an eye for visual design, so this was a 'must' for me. I wanted my website to look modern, not like a geocities website from the 90's. I searched for Web 2.0 example sites and templates and found some design guidelines. I didn't follow the design guidelines exactly, since I needed to maintain a certain degree of professionalism for a school website, but it did give me some ideas to help me along in my design process.
  • Search for CSS generators. Some of the newer CSS3 features may be harder to figure out how to use, and there are tons of generators out there that will help you get the look that you want. Also, most of these generators will generate browser-specific code to help maximize compatibility.
Not much of a guide, but these are things that have really helped me out in the past few days. The purpose of this blog was mostly just to plug jsFiddle. Seriously, it's awesome.