This hands-on lab practice is intended to help students develop a better understanding of security vulnerabilities in content providers for Android applications.
First, create a new Android Studio project and choose "Empty Activity". Click "Next"
Name it “ContentProvider” with the company domain of "com.example" and click on Next
Right Click on "com.example.ContentProvider--> New-->Java Class"
Name the class as "StudentsProvider"
Copy and paste all the following code in the specified file
//Copy and paste the following code into “MainActivity.java”.
//MainActivity.java
package com.example.contentprovider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private TextView display;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
display = (TextView)findViewById(R.id.txtDisplay);
}
public void onClickAddName(View view) {
// Add a new student record
ContentValues values = new ContentValues();
values.put(StudentsProvider.NAME,
((EditText)findViewById(R.id.txtName)).getText().toString());
values.put(StudentsProvider.GRADE,
((EditText)findViewById(R.id.txtGrade)).getText().toString());
Uri uri = getContentResolver().insert(
StudentsProvider.CONTENT_URI, values);
Toast.makeText(getBaseContext(),
uri.toString(), Toast.LENGTH_LONG).show();
}
public void onClickRetrieveStudents(View view) {
// Retrieve student records
String URL = "content://com.example.contentprovider.College/students";
Uri students = Uri.parse(URL);
Cursor c = managedQuery(students, null, null, null, "name");
StringBuilder stringBuilder = new StringBuilder("");
if (c.moveToFirst()) {
do{
stringBuilder.append(c.getString(c.getColumnIndex(StudentsProvider._ID)) +
", " + c.getString(c.getColumnIndex( StudentsProvider.NAME)) +
", " + c.getString(c.getColumnIndex( StudentsProvider.GRADE)) + "\n");
/*Toast.makeText(this,
c.getString(c.getColumnIndex(StudentsProvider._ID)) +
", " + c.getString(c.getColumnIndex( StudentsProvider.NAME)) +
", " + c.getString(c.getColumnIndex( StudentsProvider.GRADE)),
Toast.LENGTH_SHORT).show();*/
} while (c.moveToNext());
display.setText(stringBuilder.toString());
}
else {
display.setText("No students or grades.");
}
}
public void onClickClearDisplay(View view) {
display.setText("");
}
}
//Copy the following code into the newly created “StudentsProvider.java”.
//StudentsProvider.java
package com.example.contentprovider;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import java.util.HashMap;
public class StudentsProvider extends ContentProvider {
static final String PROVIDER_NAME = "com.example.contentprovider.College";
static final String URL = "content://" + PROVIDER_NAME + "/students";
static final Uri CONTENT_URI = Uri.parse(URL);
static final String _ID = "_id";
static final String NAME = "name";
static final String GRADE = "grade";
private static HashMap<String, String> STUDENTS_PROJECTION_MAP;
static final int STUDENTS = 1;
static final int STUDENT_ID = 2;
static final UriMatcher uriMatcher;
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "students", STUDENTS);
uriMatcher.addURI(PROVIDER_NAME, "students/#", STUDENT_ID);
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
static final String DATABASE_NAME = "College";
static final String STUDENTS_TABLE_NAME = "students";
static final int DATABASE_VERSION = 1;
static final String CREATE_DB_TABLE =
" CREATE TABLE " + STUDENTS_TABLE_NAME +
" (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" name TEXT NOT NULL, " +
" grade TEXT NOT NULL);";
/**
* Helper class that actually creates and manages
* the provider's underlying data repository.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL(CREATE_DB_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + STUDENTS_TABLE_NAME);
onCreate(db);
}
}
@Override
public boolean onCreate() {
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
/**
* Create a write able database which will trigger its
* creation if it doesn't already exist.
*/
db = dbHelper.getWritableDatabase();
return (db == null)? false:true;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
/**
* Add a new student record
*/
long rowID = db.insert( STUDENTS_TABLE_NAME, "", values);
/**
* If record is added successfully
*/
if (rowID > 0)
{
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
return _uri;
}
throw new SQLException("Failed to add a record into " + uri);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(STUDENTS_TABLE_NAME);
switch (uriMatcher.match(uri)) {
case STUDENTS:
qb.setProjectionMap(STUDENTS_PROJECTION_MAP);
break;
case STUDENT_ID:
qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (sortOrder == null || sortOrder == ""){
/**
* By default sort on student names
*/
sortOrder = NAME;
}
Cursor c = qb.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
/**
* register to watch a content URI for changes
*/
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)){
case STUDENTS:
count = db.delete(STUDENTS_TABLE_NAME, selection, selectionArgs);
break;
case STUDENT_ID:
String id = uri.getPathSegments().get(1);
count = db.delete( STUDENTS_TABLE_NAME, _ID + " = " + id +
(!TextUtils.isEmpty(selection) ? " AND (" +
selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)){
case STUDENTS:
count = db.update(STUDENTS_TABLE_NAME, values,
selection, selectionArgs);
break;
case STUDENT_ID:
count = db.update(STUDENTS_TABLE_NAME, values, _ID +
" = " + uri.getPathSegments().get(1) +
(!TextUtils.isEmpty(selection) ? " AND (" +
selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri );
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
/**
* Get all student records
*/
case STUDENTS:
return "vnd.android.cursor.dir/vnd.example.students";
/**
* Get a particular student
*/
case STUDENT_ID:
return "vnd.android.cursor.item/vnd.example.students";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
}
//Copy the following code into “activity_main.xml”. Make sure to click on the “Text” tab at the bottom left of the “activity_main.xml” window.
//activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Name"/>
<EditText
android:id="@+id/txtName"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Grade"/>
<EditText
android:id="@+id/txtGrade"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
<Button
android:text="Add Name"
android:id="@+id/btnAdd"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="onClickAddName" />
<Button
android:text="Retrieve Students"
android:id="@+id/btnRetrieve"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="onClickRetrieveStudents" />
<Button
android:text="Clear Display"
android:id="@+id/btnClear"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="onClickClearDisplay" />
<TextView
android:id="@+id/txtDisplay"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
//Copy the following code into "AndroidManifest.xml".
//AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.contentprovider">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ContentProvider">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:authorities="com.example.contentprovider.College"
android:name=".StudentsProvider"
android:exported="true"/>
</application>
</manifest>
Save the project and run it on the AVD that shall show this interface
We will enter some data into our database. You can enter any Name and Grade you wish and click on "Add Name" button
Save the project and run it on the AVD that shall show this interface
We will enter some data into our database. You can enter any Name and Grade you wish and click on "Add Name" button
We will enter some data into our database. You can enter any Name and Grade you wish and click on "Add Name" button
The ContentProvider application is the base program that allows you to insert student names and grades into a database, and it contains a content provider which helps the application manage access to the data stored by itself and provide a way to share data with other applications.
Now we will create a second application named AccessProvider to use the content provider in the previous application to obtain some data stored in ContentProvider. Create a new Android Studio project by going to File ->New->New Project...
First, create a new Android Studio project and choose "Empty Activity". Click "Next"
Name it “AccessProvider” with the company domain of "example.com" and click on Next
Copy and paste all the following code in the specified file
//Copy and paste the following code into “MainActivity.java” of AccessProvider.
//MainActivity.java - Access Provider
package com.example.accessprovider;
import android.database.Cursor;
import android.net.Uri;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private TextView display;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText ed1 = (EditText) findViewById(R.id.editText);
final Button bt1 = (Button) findViewById(R.id.btnDisplay);
final Button bt2 = (Button) findViewById(R.id.btnRetrieveName);
final Button bt3 = (Button) findViewById(R.id.btnClear);
display = (TextView) findViewById(R.id.txtDisplay);
bt2.setOnClickListener(new View.OnClickListener() { //retrieve
@Override
public void onClick(View v) {
String URL = "content://com.example.contentprovider.College/students";
Uri students = Uri.parse(URL);
String input = ed1.getText().toString();
//Uri uri, String[] projection, String selection,
// String[] selectionArgs, String sortOrder
String [] mProjection = {"_id", "name", "grade"};
String mSelectionClause = "name ='" + input +"'";
Cursor c = getContentResolver().query(students,mProjection ,mSelectionClause,
null,null);
//Cursor c = (students, mSelectionClause, null, null, null);
StringBuilder stringBuilder = new StringBuilder("RETRIEVING INFO\n");
if (c.moveToFirst()) {
do{
String id = c.getColumnName(0);
String name = c.getColumnName(1); //c.getString(c.getColumnIndex( StudentsProvider.NAME));
String grade = c.getColumnName(2); //c.getString(c.getColumnIndex( StudentsProvider.GRADE));
String val1 = c.getString(0);
String val2 = c.getString(1);
String val3 = c.getString(2);
stringBuilder.append("_id: " + val1 + ", name: "+ val2+
", grade: " + val3 + "\n");
/*Toast.makeText(getApplicationContext(),
"_id: " + val1 + ", name: "+ val2+
", grade: " + val3, Toast.LENGTH_SHORT).show();*/
} while (c.moveToNext());
stringBuilder.append("Total record(s) found :" + c.getCount());
/*Toast.makeText(getApplicationContext(),
"total record found :" + c.getCount(), Toast.LENGTH_SHORT).show();*/
display.setText(stringBuilder.toString());
}
else {
display.setText("No records found.");
}
}
});
bt1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String URL = "content://com.example.contentprovider.College/students";
Uri students = Uri.parse(URL);
Cursor c = managedQuery(students, null, null, null, "name");
StringBuilder stringBuilder = new StringBuilder("DISPLAYING ALL CONTENTS\n");
if (c.moveToFirst()) {
do{
String id = c.getColumnName(0);
String name = c.getColumnName(1); //c.getString(c.getColumnIndex( StudentsProvider.NAME));
String grade = c.getColumnName(2); //c.getString(c.getColumnIndex( StudentsProvider.GRADE));
String val1 = c.getString(0);
String val2 = c.getString(1);
String val3 = c.getString(2);
stringBuilder.append("_id: " + val1 + ", name: "+ val2+
", grade: " + val3 + "\n");
/*Toast.makeText(getApplicationContext(),
"id:" + val1 + "name:"+ val2+
", grade:" + val3, Toast.LENGTH_SHORT).show();*/
} while (c.moveToNext());
stringBuilder.append("Total record(s) found :" + c.getCount());
/*Toast.makeText(getApplicationContext(),
"total record found :" + c.getCount(), Toast.LENGTH_SHORT).show();*/
display.setText(stringBuilder.toString());
}
else {
display.setText("No records found.");
}
}
});
bt3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
display.setText("");
}
});
}
}
//Copy the following code into “activity_main.xml” of AccessProvider. Make sure to click on the “Text” tab at the bottom left of the “activity_main.xml” window.
//activity_main.xml - AccessProvider
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/editText"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
<Button
android:text="Display all contents of the table"
android:id="@+id/btnDisplay"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:text="Retrieve info for given name"
android:id="@+id/btnRetrieveName"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:text="Clear Display"
android:id="@+id/btnClear"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/txtDisplay"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Save the project and run it on the AVD that shall show this interface
If we click on "Display all Contents of the Table", all the inserted data will show like this
If we Enter a name or grade we entered and click on "Retrieve info for Given Name", all the matching data will show like this