Add management of Unique App Identifier (UAID). This help identify app
install threw other google play client (Yalp, Aurora,...) Fix #7 Thanks @stom79 for working on most of what's in Utils.java
This commit is contained in:
parent
416a3b4583
commit
1e65884d4e
|
@ -0,0 +1,82 @@
|
|||
package org.eu.exodus_privacy.exodusprivacy;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class Utils {
|
||||
|
||||
@SuppressLint("PackageManagerGetSignatures")
|
||||
public static String getCertificateSHA1Fingerprint(PackageManager pm, String packageName) {
|
||||
int flags = PackageManager.GET_SIGNATURES;
|
||||
PackageInfo packageInfo = null;
|
||||
try {
|
||||
packageInfo = pm.getPackageInfo(packageName, flags);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
assert packageInfo != null;
|
||||
Signature[] signatures = packageInfo.signatures;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(packageName);
|
||||
|
||||
|
||||
for(Signature signature: signatures) {
|
||||
InputStream input = new ByteArrayInputStream(signature.toByteArray());
|
||||
CertificateFactory cf = null;
|
||||
try {
|
||||
cf = CertificateFactory.getInstance("X509");
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
assert cf != null;
|
||||
X509Certificate c = (X509Certificate) cf.generateCertificate(input);
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
byte[] publicKey = md.digest(c.getEncoded());
|
||||
builder.append(' ');
|
||||
builder.append(byte2HexFormatted(publicKey).toUpperCase());
|
||||
} catch (NoSuchAlgorithmException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
String hexString = null;
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
byte[] publicKey = md.digest(builder.toString().getBytes());
|
||||
hexString = byte2HexFormatted(publicKey);
|
||||
} catch (NoSuchAlgorithmException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
assert hexString != null;
|
||||
return hexString.toUpperCase();
|
||||
}
|
||||
|
||||
private static String byte2HexFormatted(byte[] arr) {
|
||||
StringBuilder str = new StringBuilder(arr.length * 2);
|
||||
for (byte anArr : arr) {
|
||||
String h = Integer.toHexString(anArr);
|
||||
int l = h.length();
|
||||
if (l == 1) h = "0" + h;
|
||||
if (l > 2) h = h.substring(l - 2, l);
|
||||
str.append(h.toUpperCase());
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
}
|
|
@ -28,10 +28,12 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import org.eu.exodus_privacy.exodusprivacy.R;
|
||||
import org.eu.exodus_privacy.exodusprivacy.Utils;
|
||||
import org.eu.exodus_privacy.exodusprivacy.databinding.AppItemBinding;
|
||||
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
|
||||
import org.eu.exodus_privacy.exodusprivacy.objects.Report;
|
||||
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -48,6 +50,7 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
|
|||
private String filter = "";
|
||||
private final int HIDDEN_APP = 0;
|
||||
private final int DISPLAYED_APP = 1;
|
||||
private Context context;
|
||||
|
||||
|
||||
private Comparator<PackageInfo> alphaPackageComparator = new Comparator<PackageInfo>() {
|
||||
|
@ -59,8 +62,9 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
};
|
||||
|
||||
public ApplicationListAdapter(PackageManager manager, OnAppClickListener listener) {
|
||||
public ApplicationListAdapter(Context context, PackageManager manager, OnAppClickListener listener) {
|
||||
onAppClickListener = listener;
|
||||
this.context = context;
|
||||
setPackageManager(manager);
|
||||
}
|
||||
|
||||
|
@ -75,7 +79,11 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
|
|||
List<PackageInfo> toRemove = new ArrayList<>();
|
||||
for (PackageInfo pkg : packages) {
|
||||
if (!gStore.equals(packageManager.getInstallerPackageName(pkg.packageName))) {
|
||||
toRemove.add(pkg);
|
||||
String auid = Utils.getCertificateSHA1Fingerprint(packageManager,pkg.packageName);
|
||||
String appuid = DatabaseManager.getInstance(context).getAUID(pkg.packageName);
|
||||
if(!auid.equalsIgnoreCase(appuid)) {
|
||||
toRemove.add(pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
packages.removeAll(toRemove);
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.content.pm.PackageInfo;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
|
@ -64,7 +65,7 @@ public class AppListFragment extends Fragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
applistBinding = DataBindingUtil.inflate(inflater,R.layout.applist,container,false);
|
||||
return applistBinding.getRoot();
|
||||
}
|
||||
|
@ -87,7 +88,7 @@ public class AppListFragment extends Fragment {
|
|||
}
|
||||
applistBinding.noPackageManager.setVisibility(View.GONE);
|
||||
applistBinding.noAppFound.setVisibility(View.GONE);
|
||||
adapter = new ApplicationListAdapter(packageManager, onAppClickListener);
|
||||
adapter = new ApplicationListAdapter(getActivity().getApplicationContext(),packageManager, onAppClickListener);
|
||||
if(adapter.getItemCount() == 0) {
|
||||
applistBinding.noAppFound.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
|
|
@ -44,13 +44,13 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
|
||||
public static DatabaseManager getInstance(Context context) {
|
||||
if(instance == null)
|
||||
instance = new DatabaseManager(context,"Exodus.db",null,1);
|
||||
instance = new DatabaseManager(context,"Exodus.db",null,2);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL("Create Table if not exists applications (id INTEGER primary key autoincrement, package TEXT, name TEXT, creator TEXT);");
|
||||
db.execSQL("Create Table if not exists applications (id INTEGER primary key autoincrement, package TEXT, name TEXT, creator TEXT, auid TEXT);");
|
||||
db.execSQL("Create Table if not exists reports (id INTEGER primary key, creation INTEGER, updateat INTEGER, downloads TEXT, version TEXT, version_code INTEGER, app_id INTEGER, foreign key(app_id) references applications(id));");
|
||||
db.execSQL("Create Table if not exists trackers (id INTEGER primary key, name TEXT, creation_date INTEGER, code_signature TEXT, network_signature TEXT, website TEXT, description TEXT);");
|
||||
|
||||
|
@ -60,6 +60,9 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// do nothing
|
||||
if(newVersion >= 2) {
|
||||
db.execSQL("Alter Table applications add column auid TEXT");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean existReport(SQLiteDatabase db, long reportId) {
|
||||
|
@ -119,6 +122,7 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
values.put("package", application.packageName);
|
||||
values.put("name",application.name);
|
||||
values.put("creator",application.creator);
|
||||
values.put("auid",application.auid);
|
||||
|
||||
if(!existApplication(db, application.packageName)) {
|
||||
db.insert("applications", null, values);
|
||||
|
@ -309,4 +313,18 @@ public class DatabaseManager extends SQLiteOpenHelper {
|
|||
insertOrUpdateTracker(db,tracker);
|
||||
}
|
||||
}
|
||||
|
||||
public String getAUID(String packageName) {
|
||||
String where = "package = ?";
|
||||
String[] whereArgs = {packageName};
|
||||
String[] columns = {"auid"};
|
||||
Cursor cursor = getReadableDatabase().query("applications",columns,where,whereArgs,null,null,null,null);
|
||||
String uaid="";
|
||||
if(cursor.moveToFirst())
|
||||
{
|
||||
uaid = cursor.getString(0);
|
||||
}
|
||||
cursor.close();
|
||||
return uaid;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,10 +42,14 @@ import java.text.DateFormat;
|
|||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
@ -218,13 +222,14 @@ public class NetworkManager {
|
|||
|
||||
|
||||
if(object != null) {
|
||||
List<String> handles = new ArrayList<>();
|
||||
Map<String,String> handles = new HashMap<>();
|
||||
try {
|
||||
JSONArray applications = object.getJSONArray("applications");
|
||||
for(int i = 0; i<applications.length(); i++) {
|
||||
JSONObject app = applications.getJSONObject(i);
|
||||
String handle = app.getString("handle");
|
||||
handles.add(handle);
|
||||
String auid = app.getString("app_uid");
|
||||
handles.put(handle,auid);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
mes.listener.onError(mes.context.getString(R.string.json_error));
|
||||
|
@ -234,29 +239,30 @@ public class NetworkManager {
|
|||
mes.listener.onSuccess();
|
||||
}
|
||||
|
||||
private void getReports(Message mes, List<String> handles) {
|
||||
private void getReports(Message mes, Map<String,String> handles) {
|
||||
ArrayList<String> packages = mes.args.getStringArrayList("packages");
|
||||
if(packages == null)
|
||||
return;
|
||||
|
||||
packages.retainAll(handles);
|
||||
packages.retainAll(handles.keySet());
|
||||
|
||||
// Add some random packages to avoid tracking
|
||||
Random rand = new Random(Thread.currentThread().getId());
|
||||
int alea = rand.nextInt(120) % 10 + 11;
|
||||
List<String> handleList = new ArrayList<>(handles.keySet());
|
||||
for(int i = 0 ; i < alea; i++) {
|
||||
int val = rand.nextInt(handles.size());
|
||||
packages.add(handles.get(val));
|
||||
int val = rand.nextInt(handleList.size());
|
||||
packages.add(handleList.get(val));
|
||||
}
|
||||
|
||||
|
||||
for(int i = 0; i < packages.size(); i++) {
|
||||
mes.listener.onProgress(R.string.parse_application,i+1,packages.size());
|
||||
getReport(mes,packages.get(i));
|
||||
getReport(mes,packages.get(i),handles.get(packages.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
private void getReport(Message mes, String handle) {
|
||||
private void getReport(Message mes, String handle, String auid) {
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(apiUrl+"search/"+handle);
|
||||
|
@ -272,6 +278,7 @@ public class NetworkManager {
|
|||
ArrayList<String> packages = mes.args.getStringArrayList("packages");
|
||||
if(packages != null && packages.contains(handle)) {
|
||||
Application app = parseApplication(application, handle);
|
||||
app.auid = auid;
|
||||
DatabaseManager.getInstance(mes.context).insertOrUpdateApplication(app);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
|
@ -285,6 +292,7 @@ public class NetworkManager {
|
|||
application.packageName = packageName;
|
||||
application.creator = object.getString("creator");
|
||||
application.name = object.getString("name");
|
||||
|
||||
//parse Report
|
||||
application.reports = new HashSet<>();
|
||||
JSONArray reports = object.getJSONArray("reports");
|
||||
|
|
|
@ -26,6 +26,7 @@ public class Application {
|
|||
public String name;
|
||||
public String creator;
|
||||
public Set<Report> reports;
|
||||
public String auid;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
@ -35,6 +36,7 @@ public class Application {
|
|||
Application that = (Application) o;
|
||||
|
||||
if (id != that.id) return false;
|
||||
if (!auid.equals(that.auid)) return false;
|
||||
return packageName.equals(that.packageName);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue