Open In App

Tips For Making Secured Android Apps

Last Updated : 20 Jul, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Android apps are becoming a necessity in this world. From business, companies, education, collaboration, personal blogs, foods and groceries, health and medicine, social media platforms, accessing Government Services and Digital payments and even voting everything is available on the Internet. But how to make our Android apps secure? So in this article, we are going to provide some tips to make your Android apps secure.

1. How to Check When Someone Is Installing the app such that the Device is rooted or not

First, you should know what is android rooting so by default app comes with certain limitations by the manufacturer of the app and operating system and rooting allows users to overcome these restrictions and get some extra control and customizable options and you can check in your Android app if someone is installing the app the user device is rooted or not. If rooted then you can just finish the activity. To know more about Rooting: What is Android Rooting?

Java




package com.example.gfgapp;
 
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;
 
import androidx.appcompat.app.AppCompatActivity;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        boolean isRooted = checkRoot();
 
        if (isRooted) {
            Toast.makeText(this, "Your device is rooted", Toast.LENGTH_LONG).show();
            // Finishing the activity if root is detected
            new Handler().postDelayed(() -> finish(), 2000);
        }
    }
 
    private boolean checkRoot() {
        return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
    }
 
    private boolean checkRootMethod1() {
        String[] paths = {"/system/app/Superuser.apk", "/system/app/SuperSU.apk"};
        for (String path : paths) {
            File file = new File(path);
            if (file.exists()) {
                return true;
            }
        }
        return false;
    }
 
    private boolean checkRootMethod2() {
        try {
            Process process = Runtime.getRuntime().exec(new String[]{"which", "su"});
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = reader.readLine();
            if (line != null) {
                return true;
            }
        } catch (IOException e) {
            showToast("Exception occurred: " + e.getMessage());
        }
        return false;
    }
 
    private boolean checkRootMethod3() {
        String buildTags = Build.TAGS;
        return buildTags != null && buildTags.contains("test-keys");
    }
 
    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }
}


Kotlin




package com.example.gfgapp
 
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.io.BufferedReader
import java.io.File
import java.io.IOException
import java.io.InputStreamReader
 
class MainActivity : AppCompatActivity() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        val isRooted = checkRoot()
 
        if (isRooted) {
            Toast.makeText(this, "Your device is rooted", Toast.LENGTH_LONG).show()
            // Finishing the activity if root is detected
            Handler().postDelayed({ finish() }, 2000)
        }
    }
 
    private fun checkRoot(): Boolean {
        return checkRootMethod1() || checkRootMethod2() || checkRootMethod3()
    }
 
    private fun checkRootMethod1(): Boolean {
        val paths = arrayOf("/system/app/Superuser.apk", "/system/app/SuperSU.apk")
        for (path in paths) {
            val file = File(path)
            if (file.exists()) {
                return true
            }
        }
        return false
    }
 
    private fun checkRootMethod2(): Boolean {
        try {
            val process = Runtime.getRuntime().exec(arrayOf("which", "su"))
            val reader = BufferedReader(InputStreamReader(process.inputStream))
            val line = reader.readLine()
            if (line != null) {
                return true
            }
        } catch (e: IOException) {
            showToast("Exception occurred: " + e.message)
        }
        return false
    }
 
    private fun checkRootMethod3(): Boolean {
        val buildTags = Build.TAGS
        return buildTags != null && buildTags.contains("test-keys")
    }
 
    private fun showToast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_LONG).show()
    }
}


Code Explanation:

  • checkRootMethod1(): This method checks for the known related root files (‘Superuser.apk’,’SuperSu.apk’) in the system apps directory that is /system/app/
  • checkRootMethod2() This method uses the Runtime.getRuntime().exec() method to execute the command that su in the device shell. It then reads the output of the command and checks if it is not null. If the output is not null it means that the su command exists and indicates that the device is rooted.

Note: The command su refers to the superuser. The su command is typically associated with gaining root or superuser privileges on an Android device. 

  • checkRootMethod3() This method checks for the presence of the test-keys string in the Build.TAGS field. The Build.TAGS field is a system property that contains information about the device build and the test-keys string indicates that the device build was signed with test keys which is a common practice in the process of rooting Android devices.

2. Checking User Is Using VPN To Access The App

There are many things that users can do when he/she is trying to access our app by connecting to VPN such as IP Address Masking, Network latency, Fake user current location, etc.. to prevent this in your app. 

Java




package com.example.gfgapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Bundle;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity {
 
    private NetworkChangeReceiver networkChangeReceiver;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // Creating  an instance of the NetworkChangeReceiver
          // to Monitor Network changes constantly
        networkChangeReceiver = new NetworkChangeReceiver();
 
        // Registering the receiver to listen for connectivity change broadcasts
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(networkChangeReceiver, filter);
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Unregister the receiver to avoid memory leaks
        unregisterReceiver(networkChangeReceiver);
    }
 
    // BroadcastReceiver for monitoring network changes
    private class NetworkChangeReceiver extends BroadcastReceiver {
 
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
 
            // Checking if the broadcast action is CONNECTIVITY_ACTION
            if (action != null && action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
 
                // Get the network capabilities of the active network
                NetworkCapabilities nc = cm.getNetworkCapabilities(cm.getActiveNetwork());
 
                // Check if the active network has VPN transport capability
                  // That is it is connected to vpn
                if (nc != null && nc.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
                    Toast.makeText(context, "Please don't use VPN", Toast.LENGTH_LONG).show();
                    // Finishing the activity
                    finish();
                }
            }
        }
    }
}


Kotlin




package com.example.gfgapp
 
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
 
class MainActivity : AppCompatActivity() {
 
    private lateinit var networkChangeReceiver: NetworkChangeReceiver
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        // Creating an instance of the NetworkChangeReceiver
          // to Monitor Network changes constantly
        networkChangeReceiver = NetworkChangeReceiver()
 
        // Registering the receiver to listen for connectivity change broadcasts
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        registerReceiver(networkChangeReceiver, filter)
    }
 
    override fun onDestroy() {
        super.onDestroy()
        // Unregister the receiver to avoid memory leaks
        unregisterReceiver(networkChangeReceiver)
    }
 
    // BroadcastReceiver for monitoring network changes
    private inner class NetworkChangeReceiver : BroadcastReceiver() {
 
        override fun onReceive(context: Context, intent: Intent) {
            val action = intent.action
 
            // Checking if the broadcast action is CONNECTIVITY_ACTION
            if (action != null && action == ConnectivityManager.CONNECTIVITY_ACTION) {
                val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
 
                // Get the network capabilities of the active network
                val nc = cm.getNetworkCapabilities(cm.activeNetwork)
 
                // Check if the active network has VPN transport
                 // capability (i.e., it is connected to a VPN)
                if (nc != null && nc.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
                    Toast.makeText(context, "Please don't use VPN", Toast.LENGTH_LONG).show()
                    // Finishing the activity
                    finish()
                }
            }
        }
    }
}


In this code, we are constantly checking if the user is connected to VPN or not if connected to VPN then it automatically app stops running giving one 

Output:

If connected to VPN:

3. Hiding All The Content Of The App When There Is No Internet

The first major importance of hiding the app content when there is no internet is that it prevents unauthorized access, protects user privacy, reduces vulnerabilities, and enhances the user experience. By hiding the content sensitive information remains inaccessible, minimizing the risk of data breaches and unauthorized access. It also reduces the app’s exposure to potential security threats. So we have created a basic activity_main.xml for demonstration purposes and used frame layout so that all the things that are present in the frame layout will be hidden when there is no internet you can add as many things inside the frame layout and we are using LottieAnimation to display the Animation when there is no internet To Know More or To Implement the Lottie animation.

activity_main.xml:

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <FrameLayout
        android:id="@+id/main_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
 
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/gfg"
            android:layout_marginBottom="17dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/linearLayout"
            app:layout_constraintVertical_bias="0.5"/>
         
 
        <LinearLayout
            android:id="@+id/linearLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintTop_toBottomOf="@+id/imageView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent">
 
            <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="Welcome To GFG APP"
                android:textAllCaps="true"
                android:textColor="@color/black"
                android:textSize="20sp"
                android:textStyle="bold" />
 
        </LinearLayout>
 
    </FrameLayout>
 
    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/animation_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"
        app:lottie_autoPlay="true"
        app:lottie_loop="true"
        app:lottie_rawRes="@raw/nc4"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
 
    <!-- This raw will be your animation that will be
         displayed if there is no internet connection-->
 
</androidx.constraintlayout.widget.ConstraintLayout>


MainActivity.Java:

Java




package com.example.gfgapp;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
 
import androidx.appcompat.app.AppCompatActivity;
 
import com.airbnb.lottie.LottieAnimationView;
 
public class MainActivity extends AppCompatActivity {
 
    private FrameLayout mainLayout;
    private LottieAnimationView animationView;
 
    private NetworkChangeReceiver networkChangeReceiver;
 
    @Override
    @SuppressWarnings("deprecation")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mainLayout = findViewById(R.id.main_layout);
        animationView = findViewById(R.id.animation_view);
 
        // Checking if the device is not connected to the internet
        if (!isConnected()) {
            // Hiding the main layout and show the lottie animation
            mainLayout.setVisibility(View.GONE);
            animationView.setVisibility(View.VISIBLE);
            animationView.setAnimation(R.raw.nc4);
            animationView.loop(true);
            animationView.playAnimation();
        }
 
        networkChangeReceiver = new NetworkChangeReceiver();
        IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        // Registering the network change receiver to
          // listen for connectivity changes
        registerReceiver(networkChangeReceiver, intentFilter);
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Unregistering the network change receiver
          // when the activity is destroyed
        unregisterReceiver(networkChangeReceiver);
    }
 
    // Checking if the device is connected to the internet or not
    private boolean isConnected() {
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        return networkInfo != null && networkInfo.isConnected();
    }
 
    // BroadcastReceiver to listen for network connectivity changes
    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        @SuppressWarnings("deprecation")
        public void onReceive(Context context, Intent intent) {
            // Checking if the device is connected to the internet
            if (isConnected()) {
                // Showing the main layout and hiding the lottie animation
                Toast.makeText(MainActivity.this,"Connected To Internet",Toast.LENGTH_SHORT).show();
                mainLayout.setVisibility(View.VISIBLE);
                animationView.setVisibility(View.GONE);
                animationView.cancelAnimation();
            } else {
                // Hiding the main layout and showing the lottie animation
                Toast.makeText(MainActivity.this,"No Internet",Toast.LENGTH_SHORT).show();
                mainLayout.setVisibility(View.GONE);
                animationView.setVisibility(View.VISIBLE);
                animationView.setAnimation(R.raw.nc4);
                animationView.loop(true);
                animationView.playAnimation();
            }
        }
    }
}


Output:

4. Checking For Developer Mode

When Developer Mode is Enabled User Can Modify The Code or Behavior of the app. This can include injecting malicious code, introducing vulnerabilities, or bypassing security measures present in the app. Developer mode allows users to grant additional permissions to apps beyond what is typically available in the app. This means an unauthorized user can grant excessive permissions to an app potentially compromising user privacy and security.

So Below Is the code to check If Developer Mode Is enabled In the Device then automatically The Activity Will Close With Displaying One Toast Message. Also Added one More Thing that will check for developer Mode in the interval of 5 seconds so that if the user Turn On The Developer Mode After Entering the app.

Basic activity_main.xml file:

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    tools:context=".MainActivity">
 
    <ImageView
        android:id="@+id/gfgImage"
        android:layout_width="130dp"
        android:layout_height="130dp"
        android:contentDescription="@string/app_name"
        android:src="@drawable/gfg"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.548"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.355" />
 
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Welcome To The App"
        android:textColor="@color/black"
        android:textSize="20sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.595"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.602" />
 
</androidx.constraintlayout.widget.ConstraintLayout>


Working On MainActivity.java file also added Kotlin Code

Java




package com.ayush.gfgapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity {
      // You Can Specify Time Interval here in MiliSeconds
      // Checking every 5 seconds
    private static final long CHECK_INTERVAL = 5000;
    private Handler handler;
    private Runnable checkDeveloperModeRunnable;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        handler = new Handler();
        checkDeveloperModeRunnable = new Runnable() {
            @Override
            public void run() {
                if (isDeveloperModeEnabled()) {
                    Toast.makeText(MainActivity.this, "Turn Off Developer Mode", Toast.LENGTH_SHORT).show();
                    finish(); // Closing the activity if Developer Mode Is Enabled in the Device
                } else {
                    // Continuing checking in CHECK_INTERVAL milliseconds
                    handler.postDelayed(this, CHECK_INTERVAL);
                }
            }
        };
 
        // Starting checking for developer mode
        handler.post(checkDeveloperModeRunnable);
    }
 
    private boolean isDeveloperModeEnabled() {
        return android.provider.Settings.Secure.getInt(
                getContentResolver(),
                android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Removing the runnable from the handler to avoid memory leaks
        handler.removeCallbacks(checkDeveloperModeRunnable);
    }
}


Kotlin




package com.ayush.gfgapp
 
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.widget.Toast
 
class MainActivity : AppCompatActivity() {
   
    private val CHECK_INTERVAL: Long = 5000
    private lateinit var handler: Handler
    private lateinit var checkDeveloperModeRunnable: Runnable
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        handler = Handler()
        checkDeveloperModeRunnable = object : Runnable {
            override fun run() {
                if (isDeveloperModeEnabled()) {
                    Toast.makeText(this@MainActivity, "Please disable Developer Mode", Toast.LENGTH_SHORT).show()
                    finish() // Closing the activity if Developer Mode is enabled on the device
                } else {
                    // Continuing checking after CHECK_INTERVAL milliseconds
                    handler.postDelayed(this, CHECK_INTERVAL)
                }
            }
        }
 
        // Start checking for developer mode
        handler.post(checkDeveloperModeRunnable)
    }
 
    private fun isDeveloperModeEnabled(): Boolean {
        return android.provider.Settings.Secure.getInt(
            contentResolver,
            android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0
        ) != 0
    }
 
    override fun onDestroy() {
        super.onDestroy()
        handler.removeCallbacks(checkDeveloperModeRunnable)
    }
}


Output:

  1. When Developer Mode Is Enabled By Default
  2. If Developer Mode is Disabled Then App Should Work Normally
  3. If Developer Mode Is Enabled After User Enters The app


Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads