/******************************************************************************* * Copyright (c) 2014 CodingBad. * All rights reserved. This file is part of ASA. * * ASA is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ASA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ASA. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Ayelén Chavez - ashy.on.line@gmail.com * Joaquín Rinaudo - jmrinaudo@gmail.com ******************************************************************************/ package com.thesis.asa.hook; import java.lang.reflect.Method; import java.util.Arrays; import android.os.Build; import android.os.CancellationSignal; import android.annotation.SuppressLint; import android.content.ContentProvider; import android.content.Context; import android.database.Cursor; import android.database.DatabaseUtils; import android.net.Uri; import android.provider.ContactsContract; import android.util.Log; import com.saurik.substrate.MS; import com.thesis.asa.contacts.ContactsSettings; public class ContactsHook extends Hook { private static void printQueryArgs(String tag, Object... args) { // final Cursor query( Uri uri, // String[] projection, // String selection, // String[] selectionArgs, // String sortOrder ) Log.d(tag,("URI: " + (Uri) args[0]).toString()); if (args[1] != null) { Log.d(tag, "PROJECTION: "); String[] projection = (String[]) args[1]; for (int p = 0; p<projection.length; p++) Log.d(tag, projection[p]); } if (args[2] != null) Log.d(tag, "SELECTION: "+ (String) args[2]); if (args[3] != null) { Log.d(tag, "SELECTION ARGS: "); String [] selectionArgs = (String[]) args[3]; for (int p = 0; p<selectionArgs.length; p++) Log.d(tag, selectionArgs[p]); } } private static void printResult(String tag, Cursor cursor) { Log.d(tag, DatabaseUtils.dumpCursorToString(cursor)); } public static void hook() { MS.hookClassLoad("com.android.providers.contacts.ContactsProvider2", new MS.ClassLoadHook() { @SuppressLint("NewApi") public void classLoaded(Class<?> contentProvider) { Method query; try { Class[] params; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) params = new Class[5]; else { params = new Class[6]; params[5] = CancellationSignal.class; } params[0] = Uri.class; params[1] = String[].class; params[2] = String.class; params[3] = String[].class; params[4] = String.class; query = contentProvider.getMethod("query", params); } catch (NoSuchMethodException e) { query = null; } if (query != null) { MS.hookMethod( contentProvider, query, new MS.MethodAlteration<ContentProvider, Cursor>() { public Cursor invoked( ContentProvider contentProvider, Object... args) throws Throwable { Context context = contentProvider.getContext(); Object[] filteredGroups = Hook.queryConfigurationFromASA(context, new ContactsSettings(context)); Integer[] groups = Arrays.copyOf(filteredGroups, filteredGroups.length, Integer[].class); String contactsInGroups = ContactsHook.getContactsByGroups(this, contentProvider, groups); String selection = (String) args[2]; Uri id = (Uri) args[0]; if (selection == null || selection.replaceAll("\\s","").length() == 0){ selection = ""; } else selection = " AND " + selection; if(id.toString().contains(ContactsContract.Data.CONTENT_URI.toString())){ selection = ContactsContract.Data.CONTACT_ID + " IN " + contactsInGroups + selection; } else{ if(id.toString().contains(ContactsContract.RawContacts.CONTENT_URI.toString())){ selection = ContactsContract.RawContacts.CONTACT_ID + " IN " + contactsInGroups + selection; } else{ if(id.toString().contains(ContactsContract.Contacts.CONTENT_URI.toString())){ selection = ContactsContract.Contacts._ID + " IN " + contactsInGroups + selection; } else{ //APARENTEMENTE GRUPOS selection = (String) args[2]; } } } args[2] = selection; Cursor result = invoke(contentProvider, args); return result; } }); } } }); } protected static String uriPath() { return "content://com.thesis.asa.settings/contacts_settings"; } private static String getContactsByGroups(MS.MethodAlteration<ContentProvider, Cursor> hookedMethod, ContentProvider contentProvider, Integer[] filteredGroups) throws Throwable { Object[] groupsArgs; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN){ groupsArgs = new Object[5]; } else { groupsArgs = new Object[6]; } groupsArgs[0] = ContactsContract.Data.CONTENT_URI; groupsArgs[1] = new String[] { ContactsContract.Data.CONTACT_ID, ContactsContract.Data.DATA1 }; groupsArgs[2] = ContactsContract.Data.MIMETYPE + "=?"; groupsArgs[3] = new String[] { ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE }; Cursor contactsByGroup = hookedMethod.invoke( contentProvider, groupsArgs); String contactsInGroups = new String(); contactsInGroups = contactsInGroups + "("; if (contactsByGroup != null && contactsByGroup .moveToFirst()) { int contactColumnIndex = contactsByGroup .getColumnIndex(ContactsContract.Data.CONTACT_ID); int groupColumnIndex = contactsByGroup .getColumnIndex(ContactsContract.Data.DATA1); do { int group = contactsByGroup .getInt(groupColumnIndex); for (int i = 0; i < filteredGroups.length; i++) { if (filteredGroups[i] == group) { contactsInGroups = contactsInGroups + "'" + contactsByGroup .getInt(contactColumnIndex) + "',"; break; } } } while (contactsByGroup .moveToNext()); if (contactsInGroups .length() > 1) contactsInGroups = contactsInGroups .substring( 0, contactsInGroups .length() - 1); } contactsInGroups = contactsInGroups + ")"; if (contactsByGroup != null) contactsByGroup.close(); return contactsInGroups; } }