Privacy Secure Android Keyboards

Keep your user’s input data safe

Mobile@Exxeta
Mobile App Circular
6 min readMar 4, 2024

--

Photo by Pixabay from Pexels

At first, you might ask yourself: “What is a privacy-secure keyboard in the context of an Android app and why would I need that?”. Let us answer those questions first and then take a deeper look into an exemplary implementation of checking the keyboard type in Android and Flutter. At the bottom of this article, you will also find a list of privacy-secure keyboards. Please remember that since the keyboard can't be accessed or changed on iOS, this only affects Android devices.

What is a Privacy Secure Keyboard?

Nowadays, many keyboards leverage machine learning algorithms or other ways to correct user input. Most of these methods require user input to be tracked and used as training data for the underlying machine learning algorithms. In conclusion, this means that user input data could be accessible to third parties while using an app. That can be good at first glance, since this training data would help to improve correction/autocomplete algorithms, but no one knows, how secure the data is transferred or stored and what else these third parties use it for. This almost answers the next question:

Why would you need to check for Privacy Secure Keyboards?

For many use cases, it may not be critical which keyboard is used, but in the case of apps where user input can be very sensitive, like a banking app, this matters even more. Think about a third party tracking the login data of your bank account. Would you like that? What’s even worse for us developers: Imagine how embarrassing it would be if your app was responsible for your user’s sensitive data falling into the wrong hands. We don’t have to answer this question if we act in advance by checking which keyboard the user is using when handling sensitive inputs like login data.

How can you check if a keyboard is Privacy Secure?

Luckily each Android keyboard needs to have an identifier, just like every Android app has one, e.g. com.example.identifier. Basically, custom keyboards are just apps. Now that we know keyboards have an identifier, how do we find it out? To answer this question, we first must understand that there are two ways, a custom keyboard can be deployed to an Android phone.

  1. Users install it from the Play Store
  2. It is deployed by the manufacturer (e.g. Samsung, OnePlus, etc.)

Check the Play Store

For the first case, we can just check the Google Play Store page of said keyboard and the identifier will always be in the URL. For example, Gboard — the Google Keyboard

https://play.google.com/store/apps/details?id=com.google.android.inputmethod.latin

At the end of the URL, you can already see the full identifier of said keyboard, e.g. com.google.Android.inputmethod.latin. That’s all we need for later. But now that we are here, let me show you how you can check if a keyboard from the Play Store is privacy-secure. Open a Play Store page, e.g. GBoard and scroll down a bit until you see the section Data safety.

Screenshot of the Data safety section from the GBoad Play Store Page

In this section, you can get all the info required for checking if a keyboard meets your data safety requirements. As you can see, GBoard is not sharing any data with third parties but may collect some data. The point with third-party data sharing is most important for data privacy. If you see a keyboard sharing data with third parties, better be safe than sorry; DO NOT trust them in the context of privacy-dependent apps.

Check a Keyboard deployed by the Manufacturer

In some cases, checking the manufacturer’s keyboards is more complex. In the best case, the manufacturer is using the Google keyboard. Luckily, GBoard is becoming a standard more and more. Although there are still manufacturers out there providing their own keyboards.

For the given case that the identifier of a keyboard that a manufacturer deploys is unknown, there is one obvious solution: look it up online using search engines. During our research, this has turned out to be a tedious task for some manufacturers, as there is often no trustworthy source or even any information.

The bare knuckle and time-consuming but therefore bulletproof approaches would be using Android Debug Bridge (ADB) to retrieve the keyboard identifier or using a modified version of the code snippet below to print the identifier on the terminal. Both approaches require the device. As this is a rather complex task, this would be a topic for another article. As this blog article aims to save you this headache, a list of trusted manufacturer keyboards can be found in the table below.

Code example for checking the Keyboard identifier

In this section, we provide Android and Flutter code examples showing how to check a user’s current keyboard identifier. Please remember that the code shown below is just an example of how to get the identifier. You can use the example to get an idea and customize it to fit your needs.

Android example

We will name our class KeyboardValidation and provide a list of allowed keyboards. If you wish you can also provide the list as companion object. You’ll also need to import Context and Settings:

import android.content.Context 
import android.provider.Settings

class KeyboardValidation(private val context: Context) {
private val validKeyboardIdentifiers = listOf(
"com.android.inputmethod.latin",
"com.google.android.inputmethod.latin",
"com.google.android.tts"

// ... add more identifiers
)
}

Now we can add a helper function as well as a public function to perform keyboard validation from outside.

fun getKeyboardIdentifier(): String { 
val vendor = Settings.Secure.getString(
this.context.contentResolver,
Settings.Secure.DEFAULT_INPUT_METHOD
)

return vendor.split("/").firstOrNull() ?: ""
}

fun isKeyboardValid(): Boolean {
val keyboardIdentifier = getKeyboardIdentifier()

return validKeyboardIdentifiers.contains(keyboardIdentifier)
}

The complete file should look like this:

import android.content.Context 
import android.provider.Settings

class KeyboardValidation(private val context: Context) {
private val validKeyboardIdentifiers = listOf(
"com.android.inputmethod.latin",
"com.google.android.inputmethod.latin",
"com.google.android.tts"

// ... add more identifiers
)

fun getKeyboardIdentifier(): String {
val vendor = Settings.Secure.getString(
this.context.contentResolver,
Settings.Secure.DEFAULT_INPUT_METHOD
)

return vendor.split("/").firstOrNull() ?: ""
}

fun isKeyboardValid(): Boolean {
val keyboardIdentifier = getKeyboardIdentifier()

return validKeyboardIdentifiers.contains(keyboardIdentifier)
}
}

To use this class, you can check the current keyboard inside the onCreate() method of your MainActivity.kt.

class MainActivity : AppCompatActivity() { 
// ...

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val context = LocalContext.current
val keyboardValidation = KeyboardValidation(context)

if (!keyboardValidation.isKeyboardValid()) {
// Handle keyboard validation
}
}

// ...
}

Flutter Example

In our Flutter example, we will make use of a MethodChannel to access native Kotlin code and retrieve the current keyboard identifier. This means we can use a slightly modified function of our previous Android example:

private fun getKeyboardIdentifier(): String {
val vendor = Settings.Secure.getString(
this.contentResolver,
Settings.Secure.DEFAULT_INPUT_METHOD
)
val keyboardIdentifier = vendor.split("/").firstOrNull() ?: ""

return keyboardIdentifier
}

You can also manage splitting the identifier in your Dart code, but for the sake of this tutorial, we will just leave it as it is.

Then you will need to override the configureFlutterEngine() method to access the MethodChannel.

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 
GeneratedPluginRegistrant.registerWith(flutterEngine);

MethodChannel(flutterEngine.dartExecutor, "flutterkeyboard")
.setMethodCallHandler { call, result ->
if (call.method == "getKeyboardIdentifier") {
result.success(getKeyboardIdentifier())
}
}
}

Now, the MainActivity.kt of your Flutter app should contain the following code:

import android.provider.Settings
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);

MethodChannel(
flutterEngine.dartExecutor,
"flutterkeyboard"
).setMethodCallHandler { call, result ->
if (call.method == "getKeyboardIdentifier") {
result.success(getKeyboardIdentifier())
}
}
}

private fun getKeyboardIdentifier(): String {
val vendor = Settings.Secure.getString(
this.contentResolver,
Settings.Secure.DEFAULT_INPUT_METHOD
)
val keyboardIdentifier = vendor.split("/").firstOrNull() ?: ""

return keyboardIdentifier
}
}

Finally, you will need to access the native Kotlin code from your Dart code. Therefore, we will provide a simple util class, which you can use inside your Provider, Bloc or Riverpod logic.

class KeyboardValidationUtil { 
static const MethodChannel _channel = MethodChannel('flutterkeyboard');
static const List<String> _allowedKeyboardIdentifiers = [
'com.android.inputmethod.latin',
'com.google.android.inputmethod.latin',
'com.google.android.tts',

// ... add more identifiers
];

Future<String?> getKeyboardIdentifier() async {
String? identifier;

try {
identifier = await _channel.invokeMethod('getKeyboardIdentifier');
} catch (e) {
print("An error occured while fetching the keyboard identifier");
}

return identifier;
}

Future<bool> isKeyboardValid() async {
if (Platform.isIOS) {
return true;
}

final identifier = await getKeyboardIdentifier();

if (identifier == null) {
return false;
}

return _allowedKeyboardIdentifiers.contains(identifier);
}
}

List of Privacy Secure Android Keyboards

As promised, we will share our list of privacy-safe keyboards in this section. The following table contains the identifier required to check a keyboard and which manufacturers use it. If the keyboard is a third-party keyboard, you will also find a link to the Google Play Store.

Conclusion

In this article, we have shown that custom Android keyboards can be a huge security issue for any app. The importance of protecting your user’s data aligns with the increasingly stringent data protection laws. We provided code examples for Android and Flutter, which you can use to add an extra layer of security to your app. Feel free to post additional privacy-secure keyboards in the comments. (by Jonas Klock)

--

--

Passionate people @ Exxeta. Various topics around building great solutions for mobile devices. We enjoy: creating | sharing | exchanging. mobile@exxeta.com