How to connect your Android phone to Ubuntu to do development, testing, installations or tethering – Updated

All the way back in 2010 I wrote a post how to connect your Android device to your Ubuntu computer in order to develop on it. There have been a few changes since then, so here is the updated version of that post.

Recently, out of the blue I had permission issues accessing the my phone via adb. When I ran “adb devices” I got a “no permissions” message:

$ adb devices
List of devices attached
8XV7N18328713317	no permissions (verify udev rules); see []

It gives you a link to a Google article that addresses the issue, but their solution did not work for me.

So, here are the steps you need to take to be able to connect your Android device:

  1. Enable Unknown sources and USB debugging on your device
  2. Settings -> Developer Options -> USB debugging
    Settings -> Security -> Unknown sources

    The unknown sources in needed only if you will be developing on your device and you will be installing dev apk files.

  3. Find out the vendor id and product id of your device
  4. Run “lsusb”. This will list all of the devices connected to the USB ports of your computer- mouse, keyboard, camera, etc. including your phone or tablet. Each device will have a description, so it will be easy to identify.

    In my case the phone is a Nexus 6P, identified here as a Google device:

    $ lsusb
    Bus 002 Device 008: ID 18d1:4ee7 Google Inc. 

    This device is number 008 on bus number 002.

    You can see the device object created by udev. It is a fileL /dev/bus/usb//
    In this case it would be: /dev/bus/usb/002/008

    18d1:4ee7 represents VendorID:ProductID. In this case the vendor id is 18d1 and the product id is 4ee7. The vendor id and product id are different for each device manufacturer.

  5. Set up udev rules
  6. Now that we have all the info we need, we can set up the rules.

    Create a 51-android.rules file in /etc/udev/rules.d

    $ sudo gedit /etc/udev/rules.d/51-android.rules

    In this file you should create a rule for each device you want to connect.

    ATTR{idProduct}=="4ee7", SYMLINK+="android_adb", MODE="0660", GROUP="plugdev", TAG+="uaccess", SYMLINK+="android"

    Here, replace “4ee7” with your device’s product id from step #2.

    Note the GROUP=”plugdev” entry. This will create the device object file with the plugdev group as an owner. You need to make sure that your user is part of this group. This is done in the next step.

  7. Add your user to the plugdev group
  8. sudo usermod -a -G plugdev $(id -u -n)
  9. Activate the new udev rule and restart adb
  10. $ sudo udevadm control --reload-rules
    $ sudo service udev restart
    $ sudo udevadm trigger

    Now if we list the object files we should see that our device has a group of plugdev:

    $ ls -l /dev/bus/usb/002/
    total 0
    crw-rw----+ 1 root plugdev 189, 135 May  7 21:48 008

    Also, we should have two symlinks in /dev pointing to our device object:

    $ ll /dev | grep android
    lrwxrwxrwx   1 root root          15 May  7 21:48 android -> bus/usb/002/008
    lrwxrwxrwx   1 root root          15 May  7 21:48 android_adb -> bus/usb/002/008
  11. Restart adb and check the result
  12. $ adb kill-server
    $ adb start-server
    * daemon not running. starting it now at tcp:5037 *
    * daemon started successfully *
    $ adb devices
    List of devices attached
    8XV7N18328713317	device

    As you can see, we now have access to the connected device and can begin work.

I found a github project where the community is maintaining a rules file with the most widely used Android devices. You can just copy this rules file:

Make sure to change the group from adbusers to plugdev. The line is towards the end of the file. GROUP=”adbusers” should be changed to GROUP=”plugdev”.

Then do steps 4, 5 and 6 above.

Gradle – Automatically rename the .apk file and copy the ProGuard mapping.txt file

ProGuard writes the mapping file under the build directory of the project. Under normal circumstances all files and directories under the build dirs are excluded from version control. That is a bit of an issue if you want to be able to save that file with every release build. I always forget to this manually. So, let’s do that at build time with Gradle and while we are at it, we might as well rename our apk file, so we don’t have to do that manually either.

Here is the relevant code to put in our build.gradle file:

import java.util.regex.Matcher
import java.util.regex.Pattern

buildTypes {
        release {
        debuggable false
        minifyEnabled true
        proguardFiles 'proguard.cfg'

        // Rename the apk file and copy the ProGuard mapping file to the root of the project
        applicationVariants.all { variant ->
            if (variant.getBuildType().name.equals("release")) {
                def formattedDate = new Date().format('yyyyMMddHHmmss')
                def projectName = ""
                variant.outputs.all { output ->
                        def fullName =
                        projectName = fullName.substring(0, fullName.indexOf('-'))
                        // ${} has the value of "paidRelease"
                        outputFileName = new File((String) output.outputFile.parent, (String) outputFileName.replace(".apk", "-v${variant.versionName}-${formattedDate}.apk"))
                def mappingFile = "${rootDir}/${projectName}/build/outputs/mapping/${getCurrentFlavor()}/release/mapping.txt"
                println("mappingFile:  ${mappingFile}")
                if (variant.getBuildType().isMinifyEnabled()) {
                    variant.assemble.doLast {
                        copy {
                            from "${mappingFile}"
                            into "${rootDir}"
                            rename { String fileName ->

    debug {
        debuggable true

def getCurrentFlavor() {
    Gradle gradle = getGradle()
    String  tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
    Pattern pattern;

    if( tskReqStr.contains( "assemble" ) )
        pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
        pattern = Pattern.compile("generate(\\w+)(Release|Debug)")

    Matcher matcher = pattern.matcher( tskReqStr )

    if( matcher.find() )
    else {
        println "NO MATCH FOUND"
        return "";

The above code was updated for the new Android Studio 3.0 and Gradle 3.0.

The new Gradle introduces some changes and the output.outputFile is now a read only property. So, you cannot change it starting with Gradle 3. You will get the following error:

“Cannot set the value of read-only property ‘outputFile’ for ApkVariantOutputImpl_Decorated{apkData=Main{type=MAIN, fullName=release, filters=[]}}…”

Here are the changes:


variant.outputs.each { output ->
                    def fullName =
                    projectName = fullName.substring(0, fullName.indexOf('-'))
                    // ${} has the value of "paidRelease"
                    output.outputFile = new File((String) output.outputFile.parent, (String)".apk", "-v${variant.versionName}-${formattedDate}.apk"))


variant.outputs.all { output ->
                        def fullName =
                        projectName = fullName.substring(0, fullName.indexOf('-'))
                        // ${} has the value of "paidRelease"
                        outputFileName = new File((String) output.outputFile.parent, (String) outputFileName.replace(".apk", "-v${variant.versionName}-${formattedDate}.apk"))

The Android SDK Manager fails to fetch the platform and tools list from Google

If you get the following error message when launching the Android SDK Manager:

Failed to fetch URL, reason: File not found

most likely the owner/permissions are not set correctly for the ~/.android directory.

This error message is very confusing and it took me while to find the issue.

In my case the owner of this directory and all the files and directories in it was root.

There are numerous “solutions” given to this in many forums. Most of them recommend launching Android Studio as root. That is definitely the wrong approach. It will get rid of the error, since you will no longer be faced with permissions issues, but running Android Studio as root is a vary bad idea.

Instead, you should change the owner of the ~/.android directory to your user id.

So, find the .android directory (most likely it is in your home directory). Then change the ownership recursively to your user:

cd ~
sudo chown -R myuser: .android/

You should be now good to go.

Find out what DNS requests are made in real time via command line

Since all DNS requests are over port 53, we can use the raw tcpdump utility and just look at the packets on that port:

tcpdump -vvv -s 0 -l -n port 53

Someone took the extra step to write a php script to massage that data, so that it is easier to read as well as focusing on those queries that either do not resolve or take a long time to resolve:

Android Studio- What files and directories to exclude when importing into Subversion (or Git)

Update Oct. 17, 2019:
This is the official .gitignore file maintained by JetBrains:


List of directories and files to be excluded when importing into a version control (relative to the root of the project):

.gradle (directory)
build (directory) (file)
app/build (directory)
.idea/libraries (directory)
.idea/gradle.xml (file)
.idea/workspace.xml (file)

There are a couple of different ways to have the above directories and files not present in the version control:

1. Exclude them from the initial import
2. Delete them after the import

svn delete -m "Delete .gradle dir" http://server/path/to/repository/.gradle
svn delete -m "Delete build dir" http://server/path/to/repository/build
svn delete -m "Delete app/build dir" http://server/path/to/repository/app/build
svn delete -m "Delete" http://server/path/to/repository/
svn delete -m "Delete .idea/libraries dir" http://server/path/to/repository/.idea/libraries
svn delete -m "Delete gradle.xml" http://server/path/to/repository/.idea/gradle.xml
svn delete -m "Delete workspace.xml" http://server/path/to/repository/.idea/workspace.xml

Now that we have gone through that, the version control does not have these directories and files, but locally, the project on your computer has these (if not they will be automatically generated when you first open the project in Android Studio or you build the project).

That means that we need to delete them from svn locally and tell svn locally to ignore them:

svn ps svn:ignore '.gradle build' .
svn ps svn:ignore build app
svn ps svn:ignore 'libraries gradle.xml workspace.xml' .idea

Update Oct. 23, 2018: Here is a .gitignore file that I use for all my Android Studio projects:

# Built application files

# Files for the ART/Dalvik VM

# Java class files

# Generated files

# Gradle files
# Here depending on your project you might need to include more build direcories

# Local configuration file (sdk path, etc)

# Log Files

# Android Studio Navigation editor temp files

# Android Studio captures folder

# IntelliJ

# Keystore files

# External native build folder generated in Android Studio 2.2 and later

# OSX_files

Set up a MySQL user dedicated to backups

It is not a good idea to use the root MySQL account to do backups.

So, let’s create a user dedicated exclusively to doing backups.

Log on to MySQL as root:

mysql -u root -p

Then create the new user and grant it the necessary permissions:

CREATE USER 'backup_user_name'@'localhost' IDENTIFIED BY 'my_pass';
GRANT SELECT, SHOW VIEW, RELOAD, EVENT, TRIGGER, LOCK TABLES ON *.* TO 'backup_user_name'@'localhost';

Now that we are all set up, you can do backups with that new dedicated user:

mysqldump -u backup_user_name -pmy_pass db_name | gzip > /home/ddarazha/backups/ninelets/ninelets_`date '+%Y%m%d'`.sql.gz

Batch scale images to a particular size

First install imagemagick:

sudo apt-get install imagemagick

Then CD into the directory containing the images and make a new directory that will hold all the re-sized images. In this case I called it resized:

mkdir resized

Then run the command (from the directory holding the original images):

find . -iname \*.jpg -exec convert -verbose -quality 80 -resize 1600x1200 "{}" "resized/{}" \;

The above command will find (recursively) all the jpg files in the current directory and all directories in it, then it will execute the covert command on each image and finally store the scaled image in the resized directory. You can adjust the size parameters, file extensions and target directory as you desire.

Upgrading to Apache 2.4 will prevent WebDAV listing of directories containing index files

I recently upgraded Apache from 2.2 to 2.4 and among all the expected changes that had to be made to the existing sites-available config files, I encountered an unexpected issue with the WebDAV sites.

I could connect to the sites via DAV but weirdly enough I could not open some directories with them. I would get a “405 Method Not Allowed error”.

It turns out that if a collection in a WebDAV-enabled area contains an index.html (or whatever filename is specified in DirectoryIndex – index.php, default.html, etc.) then it becomes impossible to use WebDAV methods on that collection. See bug report.

In order to fix this, you need to disable directory indexing for the WebDAV site(s).

If you still want to have directory indexing when serving regular http requests, I recommend having WebDAV configured on a different port.

I normally have development sites, server reqular requests on port 80 and have the WebDAV configured on port 443.

So, in your sites-available/site.conf file, in the corresponding VirtualHost declaration add DirectoryIndex disabled to the Directory declaration, like so:

    <Directory /path/to/my/webdav/dir>
                    Options Indexes FollowSymLinks MultiViews
                    AllowOverride all
                    Require all granted

                    DirectoryIndex disabled

Here is an example config file:

DavLockDB /webdav/DavLock/DavLock

	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/

	Alias /web /path/to/my/webdav/dir

	<Directory /path/to/my/webdav/dir>
		Options Indexes FollowSymLinks MultiViews
		AllowOverride None
		Require all granted



    DocumentRoot /var/www/

	Alias /web /path/to/my/webdav/dir

	<Directory /path/to/my/webdav/dir>
		Options Indexes FollowSymLinks MultiViews
		AllowOverride None
		Require all granted

		DirectoryIndex disabled

	<Location /web>
		DAV On
		AuthType Digest
		AuthName "the_auth_name"
		AuthUserFile /the/digest.dav
		Require valid-user

		php_value engine off 
		RewriteEngine off	

    SSLEngine on
    SSLOptions +StrictRequire

    SSLCertificateFile /etc/ssl/certs/
    SSLCertificateKeyFile /etc/ssl/private/
    SSLCACertificateFile /etc/ssl/certs/

Then just reload Apache and you will no longer have that issue:

sudo service apache2 reload

Force all HTTP traffic to HTTPS

If you have SSL installed and configured on your site, there is a little need to continue serving http traffic over port 80. Performance is no longer a big issue and now Google would reward with better ranking sites that serve all content over https.

It is very easy to achieve that on an Apache server.

1. Make sure that you have enabled mod_rewrite
2. Add the following to your .conf file for the site:

<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{HTTPS} off
	RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

The above code goes in the VirtualHost defininition for port 80. Make sure that you also have a VirtualHost definition for port 443.

In order for this to take affect, don’t forget to:

sudo service apache2 reload

Unable to create new directories or files (write access) in WebDAV with Apache2

It turns out that if you have enabled mod_rewrite on the server and you are actually doing any url re-writing for the site under which WebDAV is running, then you will not be able to create new directories or files via WebDAV.

To fix this, all you have to do is disable the mod_rewrite when connected via the dav protocol. The mod_rewrite will still be active when browsing the site.

Just add RewriteEngine off to the Location node of the sites-available file and then reload Apache. Here is an example:

<Location /webdav/prj>
                DAV On
                AuthType Digest
                AuthName ""
                AuthUserFile /webdav/digestpasswd.dav
                Require valid-user
                php_value engine off 
                RewriteEngine off

Then reload apache:

sudo service apache2 reload

Provided that your directory permissions of the WebDAV directory is set correctly (the user under which Apache is running needs to be the owner and have write permissions) you should now be able to create new directories and files.