Friday, June 21, 2019

Missing Guide for App Bundles

App Bundles is an amazing feature for Android Developers. It helps reduce the app size for quicker downloads without having to worry about building for every architecture separately. Bundletool provides command line too to help with testing App Bundles before publishing the app.

Bundletool docs seem to miss critical information to complete testing for developers today. This guide hopes to fill the missing gaps in bundletool docs and help developers migrate to App Bundles.

This blog post mainly covers 2 crucial missing options in bundletool:


  1. How to Run bundletool ?
  2. How to test real production APKs?

Running bundletool: You can download latest version of bundletool from Github. Note that this includes a jar file with name bundletool-all-X.Y.Z.jar (Where X.Y.Z indicates the version number of the bundletool). You can just bundletool as a jar file. 

Example: java -jar bundletool-all-0.10.0.jar build-apks --bundle=app.aab --output=myapks.apks


This will build single file with all required APKs for all architectures and screen sizes.

Now how to you install the required APK on your device to test the APK? Its a simple bundletool command.

Example: java -jar bundletool-all-0.10.0.jar install-apks --apks= myapks.apks


This will ensure right APK is installed for your test device. In case adb is not your system path make sure to specify where the path is for your adb.

Example: java -jar bundletool-all-0.10.0.jar install-apks --adb=/ADBABSPATH/adb --apks= myapks.apks

The problem with this is bundletool creates APKs that are signed with the debug keystore found in your user path. So how to create APKs with release signatures? bundletool has the option to generate APKs with release signature. 

Example: java -jar bundletool-all-0.10.0.jar build-apks --bundle=app.aab --output= myapks.apks --ks=/PATHTOKEYSTORE/YOURKEYSTORE.keystore --ks-pass=pass:PASSWORD1 --ks-key-alias=yourkeyalias --key-pass=pass:PASSWORD2

Hope this helps while migrating from APKs to App Bundles for you.

Monday, October 25, 2010

Secure Facebook Browsing

It is common that most websites use HTTPS for user authentication and use HTTP for everything else. This leaves users vulnerable to Cookie Hijacking. Tools like Firesheep brought this to forefront. It lets someone in your network perform Cookie Hijacking of Facebook as simple as installing Firefox Extension.

The best way to avoid this hack is to completely use HTTPS when using Facebook but the way Facebook work, even if you go to https://www.facebook.com (Secure HTTPS page), all the links still point to http://www.facebook.com (unsecure HTTP page). This leaves Facebook users vulnerable to tools like FireSheep.

Here is a Firefox Extension I wrote to solve the same problem for Firefox. Every time user visits Facebook.com, all the requests are forced to go through HTTPS even if user starts with http://www.facebook.com

Friday, October 22, 2010

Firefox Extension: Block Facebook from your life

Its impossible to be on web and not be a Facebook user these days. Even if you are not a Social Network user, Facebook is notified whenever you visit one of the more than one million sites on the web that use Facebook Connect and has a history of leaking personally identifiable information to third parties. Either way Facebook knows your web life.

Here is a Firefox Extension that completely blocks Facebook from your online life. It won't let you visit Facebook.com, It won't let Facebook track your moves using Facebook Connect.

Monday, April 12, 2010

WGET - Authentication

How to access a page using wget that requires authentication? wget is well equipped to handle multiple authentication scenario's.

HTTP Basic Athentication: To download a page that requires HTTP basic authentication use the following mechanism:
wget https://myUserName:myPassword@www.myserver.com/mypage.html
wget http://myUserName:myPassword@www.myserver.com/mypage.html

Form Post: To download a page protected by login built on form post use the following:

wget --post-data 'user=myUserName&password=myPassword' http://www.myserver/mypage.html

Form Post with multiple pages:If you need to navigate through multiple pages after authentication to get to your page, you can save cookies on form post for authentication and reuse the cookies file to access the page you want:

wget --post-data 'user=myUserName&password=myPassword' --cookies=on --keep-session-cookies --save-cookies=myCookies.txt http://www.myserver/auth

wget --cookies=on --load-cookies=myCookies.txt --keep-session-cookies --savecookies=myCookies.txt http://www.myserver/mypage.html

Wednesday, March 31, 2010

Hey Microsoft how about banning iTunes on windows?

Wouldn't it be interesting if Microsoft follows "CPU Hog" strategy from Jobs and ban iTunes on windows for installing malware that is "Memory Hog"?

Monday, March 08, 2010

Making No as Default in EXTJS Confirm Dialog

Currently there is no configuration that supports making "No" button as default for a Confirm dialog in ExtJS. So how to make no button as default?
One way to do this is to get Dialog and mark second button as default.

Here is the code snippet that makes no as default button:


var dialog = Ext.MessageBox.confirm('Confirm', 'Do you really mean it?' ,feedbackFunction).getDialog();
dialog.defaultButton = 2;
dialog.focus();

Sunday, March 07, 2010

GZIP and Save the earth

It is amazing how we think about least significant things and put in Maximum effort rather than take care of low hanging fruits first. GZIP RFC came out around 1996 and all modern browsers (HTTP/1.1 supported Browsers) support GZIP and still it is amazing how many website doesn't support such a basic trick to save 50% of their bandwidth costs. Just adding GZIP support reduces bandwidth by 50% (70% if the website is Mostly Text) resulting in huge amount of savings.

How to Enable GZIP for Apache:

1) Make sure LoadModule deflate_module modules/mod_deflate.so in your httpd.conf
2) Add the following lines to httpd.conf



SetOutputFilter DEFLATE
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|rar|zip|pdf)$ no-gzip dont-vary

Header append Vary User-Agent




and you are done. The configuration is telling Apache to GZIP every content except for Images, Zipped content and PDF files which are already in compresses format.

Next time when a browser sends request with HTTP Header:
Accept-Encoding: gzip, deflate

You webserver serves content in GZIP format and notifies the same with a proper reponse Header:
Content-Encoding: gzip

Enable GZIP on your servers today and save the Earth.

Monday, January 18, 2010

Making Jersey work with Spring

Making Jersey work with Spring simplifies JAX-RS (Restful webservices) and make Restful services development look lot easier. This is simple tutorial of how to make Jersey work with Spring3.0 (Same can be applied to Spring 2.5)

Libraries needed:
1) Spring 3.0 distribution.
2) Jersey 1.x distribution.
3) Jersey Spring 1.0.1-SNAPSHOT

Lets Inject a simple Spring Bean using Jersey @Inject annotation.

Step 1: Update web.xml
Declare the following application context configuration for Spring:



contextConfigLocation
classpath:applicationContext.xml



Configure Spring Context Listener:


org.springframework.web.context.ContextLoaderListener



Configure Spring Request Context Listener for Spring to use request scope for Spring beans:


org.springframework.web.context.request.RequestContextListener



Now declare Jersey Spring Servlet:


jerseyspring
com.sun.jersey.spi.spring.container.servlet.SpringServlet
1


jerseyspring
/resource/*



Step 2: Create applicationContext.xml
Now create applicationContext.xml to scan JAX-RS resources in a package say com.km.services and create simple bean using Spring say com.km.spring.SimpleBean



xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">






Step 3: Define your spring bean

In this case for simplification lets create a spring which does nothing but provide a method that says hello.

package com.km.spring;

public class SimpleBean {
public String sayHello() {
return "Hello my Friend";
}
}


Step 4: Create your JAX-RS resource
Now lets create the JAX-RS to which we inject the Spring Bean that is created in Step 3.

package com.km.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.sun.jersey.spi.inject.Inject;
import com.km.spring.SimpleBean;

@Path("/hello")
@Component
@Scope("request")
public class HelloResource {

@Inject private SimpleBean simpleBean;

@GET
@Produces("text/plain")
public String getMessage() {
return simpleBean.sayHello();
}
}



Its as simple as that. Now /resource/hello will result in printing "Hello my Friend" in your client.

Friday, January 08, 2010

Android Everywhere

Sun's dream of putting Java Everywhere is finally being realized but it has nothing to do with Sun and everything to do with Google.

Now Android is on Laptops, Netbooks, Mobile Phones, Video Phones, Washing Machines, Microwaves, Printers. Android is fast becoming UI device for consumer electronics.

Monday, January 04, 2010

MySpace Shame: Fix API and then talk of Developer Contest

Today MySpace announced Submissions Now Open for the MySpace Developer Challenge. Which is really great because MySpace is also trying to drive innovation from developer community on its platform. I got pretty excited to get an app going using Myspace API.

After trying to integrate MySpace OAuth for an hour without much success constantly failing in 6.3.2 section of OAuth spec title Service Provider Grants an Access Token getting a 500 Internal Server error. I refereed to MySpace forums and found that OAuth with MySpace hasn't been working since late November and MySpace is aware of issue for well over a month and still trying to Fix the issue.

Developer contest is a great idea to improve platfom but how about making API work before a contest so that developers can really develop the apps for contest?

Thursday, December 24, 2009

Disabling browser context menu in ExtJS

Many a times when ExtJs context menu (Right Click Menu) is added to components, it creates a problem as browser right click menu comes up after ExtJs right click menu is displayed. This is one of common issues posted on ExtJs forums consistently. Here is a simple way to solve the problem:

Add the following line:


Ext.getBody().on("contextmenu", Ext.emptyFn, null, {preventDefault: true});


as first line on ExtJs onReady function.

Friday, October 09, 2009

The Boy Who Harnessed the Wind

First watched this dude on John Stewart. Most fascinating thing I heard in a while and proof world is filled with Hope:

Thursday, February 19, 2009

Bye Bye Spammers

It appears after the latest attempt to drop the connection from spammers at IP level, traffic has come to normal level.

To read about the issue and resolution follow these links:
Day 1
Day 2

Graph showing traffic returning to normal:

Someone remind google what year it is



Chrome 1.0.154.48. was released on Feb 3 2009.

Chrome home page seems to have the same issue:

Tuesday, February 17, 2009

More fun with Proxy

After disabling proxy on my server, I still see traffic on my server being high (My monthly quota might not exceed at this rate but takes up 50% of Bandwidth). Looking at access log it appears that requests hasn't stopped though they are getting 403 error. So requests coming to server and 403 response by itself is making up few GB worth a data every day. So decided to block these requests at IP level rather than proxy level. First I needed to get all unique IP addresses that needs to be blocked. That was easy to considering my log format being:

61.139.105.163 - - [17/Feb/2009:05:08:48 -0700] "GET http://ad.yieldmanager.com/imp?z=10&s=425858&u=http%3A%2F%2Fwww.popflashgames.com%2Findex.html HTTP/1.0" 403 388 "http://www.popflashgames.com/index.html" "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9) Gecko/2008052906 Firefox/3.0"


So I needed to get all the 403 message lines (access error's) and get IP (first field in the log) and get unique values of those IP's. Simple uniqx command can generate that(output redirected to tmp file):

more myserver.access_log | grep ' 403 ' | cut -d' ' -f1 | sort | uniq > /tmp/block.txt


Now that I have all the IP's that need to be blocked I wrote a simple script to block all the IP's in the tmp file (using iptables -A INPUT -s IPAddress -j DROP). Here is the script:


#!/bin/bash
# /tmp/blockspam.sh
# Drop all the spammers
SPAMIPS=$(egrep -v -E "^#|^$" /tmp/block.txt)

for spamip in $SPAMIPS
do
iptables -A INPUT -s $spamip -j DROP
done


To view all the blocked IP's use the command:
iptables -L -n

Monday, February 16, 2009

Proxy turn it off

Noticed something funny on my personal webserver. I was testing some proxy settings and left proxy setting on when done. Today morning when I tried to access my server, its unreachable. Checking on the log's there seem to be too many requests proxying through my web server. All the IP's seem to originate from China and destination seems to be Ad server:

60.173.11.121 - - [16/Feb/2009:12:07:27 -0700] "GET http://ad.spot200.com/imp?Z=728x90&s=533945&_salt=1697293642&B=12&m=2&u=http%3A%2F%2Fgifttiems.com%2F&r=1 HTTP/1.0" 302 - "http://ad.spot200.com/st?ad_type=iframe&ad_size=728x90§ion=533945" "Mozilla/3.01 (compatible;)"
59.53.48.207 - - [16/Feb/2009:12:07:27 -0700] "GET http://tag.contextweb.com/TAGPUBLISH/getad.aspx?tagver=1&if=0&ca=VIEWAD&cp=512141&ct=47581&cf=300X250&cn=1&cr=200&cw=300&ch=250&cads=0&cwu=http%3A%2F%2Fgoautoshop.com&mrnd=688840 HTTP/1.1" 200 438 "http://goautoshop.com" "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)"
61.139.105.163 - - [16/Feb/2009:12:07:27 -0700] "GET http://ad.yieldmanager.com/imp?z=0&Z=0x0&s=494075&y=30 HTTP/1.1" 302 - "http%3A%2F%2Fwww.excellenceflash.com%2Findex.html" "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0)"
61.139.105.166 - - [16/Feb/2009:12:07:28 -0700] "GET http://ad.yieldmanager.com/imp?z=0&Z=0x0&s=564462&y=30 HTTP/1.1" 302 - "http%3A%2F%2Fwww.flash-animation.net%2Findex.html" "Mozilla/4.0 (compatible; MSIE 4.5; Mac_PowerPC)"



Not sure what these requests are but seems like someone is trying to bump their ad revenue. Immediately disabled proxy on my machine.

Lesson to learn: Never forget to turn off proxy settings in Apache.

Sunday, January 11, 2009

JSF and Nested Diagnostic Context

One of the important design criteria for any large concurrent system is to be able to audit and debug production logs. Any real-world web applications need to deal with multiple clients simultaneously. In a typical java web application implementation of such a system, different threads will handle different clients. A possible but discouraged approach to differentiate the logging output of one client from another consists of instantiating a new and separate logger for each client. This technique promotes the proliferation of loggers and considerably increases their management overhead.

Interleaved log output can still be meaningful if each log entry from different contexts had a distinctive stamp. This is where Nested Diagnostic Context or NDC in short come into play. Web App frameworks like JSF which provide well defined life cycle provide additional hooks to implement NDC.

The six phases of the JSF application lifecycle are as follows:
1. Restore view
2. Apply request values; process events
3. Process validations; process events
4. Update model values; process events
5. Invoke application; process events
6. Render response




JSF provides PhaseListner that can be implemented by objects that wish to be notified at the beginning and ending of processing for each standard phase of the request processing lifecycle. This can be used to add Context before Restoring View which can be removed after Response is Rendered.

Log4J provides a nested diagnostic contexts implementation using NDC class.


Lets see how these two can be used to add User Id to every log statement. Implement NDCLogger as follows:


package com.km.logging.util;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import org.apache.log4j.NDC;

/**
* Log4j NDC Logger to add Remote User in all the log entries.
*/
public class NDCLogger implements PhaseListener {


public void beforePhase(PhaseEvent e) {
PhaseId phaseId = phaseEvent.getPhaseId();
if (phaseId == PhaseId.RESTORE_VIEW) {
NDC.push((String) FacesContext.getCurrentInstance()
.getExternalContext().getRemoteUser());
}
}

public void afterPhase(PhaseEvent e) {
PhaseId phaseId = phaseEvent.getPhaseId();
if (phaseId == PhaseId.RENDER_RESPONSE) {
NDC.pop();
NDC.remove();
}
}

public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
}



Add your PhaseListener to your faces config as follows:




com.km.logging.util.NDCLogger




If configured to do so, PatternLayout and TTCCLayout instances automatically retrieve the nested diagnostic context for the current thread without any user intervention. Hence, even if a servlet is serving multiple clients simultaneously, the logs emanating from the same code (belonging to the same category) can still be distinguished because each client request will have a different NDC tag.

Saturday, January 10, 2009

Microsoft and Silly bugs

Less than a fortnight back Zune30 leap year bug caused mass suicide of all Zune30 devices. Later on it was found to be simple looping bug in Zune's Clock Driver as shown here in the code to determine year part of the date:



year = ORIGINYEAR; /* = 1980 */

while (days > 365)
{
if (IsLeapYear(year))
{
if (days > 366)
{
days -= 366;
year += 1;
}
}
else
{
days -= 365;
year += 1;
}
}



Now today an old bug that prevents IE setting cookies if domain attribute is in upper case and has odd number of characters surfaced. Obviously this is not an indication of quality at Microsoft but it is certainly fun watching world's largest software company having such silly bugs in multiple products.

Wednesday, December 24, 2008

Monday, November 03, 2008

Throwing this java.lang.Error can get you fired

Imagine your boss going through your server log file and he see's the Error being thrown as CoderMalfunctionError. The discussion might not start in a pleasant way but you both might end up laughing since it really has nothing to do with Coder Malfunctioning.

java.nio.charset.CoderMalfunctionError is thrown when either encoder or decoder is malfunctioning. Sun decided to call combination of encoder and decoder as Coder and hence the confusion.

Irrespective of this Error always remember that if a project fails its the Architect or Coder's fault :-).