Merge branch 'installed-sig' into 'master'

Cache installed signature

This will later be useful for #122 and others. Also a few more fixes related to signatures and package information.

CC @pserwylo

See merge request !158
This commit is contained in:
Daniel Martí 2015-11-07 13:01:21 +00:00
commit 3df93940c8
9 changed files with 62 additions and 51 deletions

View File

@ -30,7 +30,6 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -96,7 +95,6 @@ import org.fdroid.fdroid.net.AsyncDownloaderFromAndroid;
import org.fdroid.fdroid.net.Downloader; import org.fdroid.fdroid.net.Downloader;
import java.io.File; import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -104,8 +102,6 @@ interface AppDetailsData {
App getApp(); App getApp();
AppDetails.ApkListAdapter getApks(); AppDetails.ApkListAdapter getApks();
String getInstalledSigHash();
} }
/** /**
@ -188,8 +184,8 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
return getString(R.string.app_not_installed); return getString(R.string.app_not_installed);
} }
// Definitely installed this version. // Definitely installed this version.
if (mInstalledSigID != null && apk.sig != null if (app.installedSig != null && apk.sig != null
&& apk.sig.equals(mInstalledSigID)) { && apk.sig.equals(app.installedSig)) {
return getString(R.string.app_installed); return getString(R.string.app_installed);
} }
// Installed the same version, but from someplace else. // Installed the same version, but from someplace else.
@ -447,9 +443,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
} }
// The signature of the installed version.
private String mInstalledSigID;
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
@ -601,7 +594,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
* like that) and then finish the activity. * like that) and then finish the activity.
*/ */
private void setApp(App newApp) { private void setApp(App newApp) {
if (newApp == null) { if (newApp == null) {
Toast.makeText(this, R.string.no_such_app, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.no_such_app, Toast.LENGTH_LONG).show();
finish(); finish();
@ -612,21 +604,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
startingIgnoreAll = app.ignoreAllUpdates; startingIgnoreAll = app.ignoreAllUpdates;
startingIgnoreThis = app.ignoreThisUpdate; startingIgnoreThis = app.ignoreThisUpdate;
// Get the signature of the installed package...
mInstalledSigID = null;
if (app.isInstalled()) {
try {
PackageInfo pi = mPm.getPackageInfo(app.id, PackageManager.GET_SIGNATURES);
Hasher hash = new Hasher("MD5", pi.signatures[0].toCharsString().getBytes());
mInstalledSigID = hash.getHash();
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Failed to get installed signature");
} catch (NoSuchAlgorithmException e) {
Log.w(TAG, "Failed to calculate signature MD5 sum");
}
}
} }
private void refreshApkList() { private void refreshApkList() {
@ -853,8 +830,8 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
alert.show(); alert.show();
return; return;
} }
if (mInstalledSigID != null && apk.sig != null if (app.installedSig != null && apk.sig != null
&& !apk.sig.equals(mInstalledSigID)) { && !apk.sig.equals(app.installedSig)) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.SignatureMismatch).setPositiveButton( builder.setMessage(R.string.SignatureMismatch).setPositiveButton(
R.string.ok, R.string.ok,
@ -1044,11 +1021,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
return adapter; return adapter;
} }
@Override
public String getInstalledSigHash() {
return mInstalledSigID;
}
public static class AppDetailsSummaryFragment extends Fragment { public static class AppDetailsSummaryFragment extends Fragment {
protected final Preferences prefs; protected final Preferences prefs;
@ -1092,10 +1064,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
return data.getApks(); return data.getApks();
} }
protected String getInstalledSigHash() {
return data.getInstalledSigHash();
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState); super.onCreateView(inflater, container, savedInstanceState);
@ -1408,11 +1376,11 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
return; return;
} }
App app = getApp();
TextView signatureView = (TextView) view.findViewById(R.id.signature); TextView signatureView = (TextView) view.findViewById(R.id.signature);
String sig = getInstalledSigHash(); if (prefs.expertMode() && !TextUtils.isEmpty(app.installedSig)) {
if (prefs.expertMode() && !TextUtils.isEmpty(sig)) {
signatureView.setVisibility(View.VISIBLE); signatureView.setVisibility(View.VISIBLE);
signatureView.setText("Signed: " + sig); signatureView.setText("Signed: " + app.installedSig);
} else { } else {
signatureView.setVisibility(View.GONE); signatureView.setVisibility(View.GONE);
} }

View File

@ -100,6 +100,8 @@ public class App extends ValueObject implements Comparable<App> {
public Apk installedApk; // might be null if not installed public Apk installedApk; // might be null if not installed
public String installedSig;
public boolean system; public boolean system;
public boolean updatedSystemApp; public boolean updatedSystemApp;
@ -209,6 +211,9 @@ public class App extends ValueObject implements Comparable<App> {
case AppProvider.DataColumns.InstalledApp.VERSION_NAME: case AppProvider.DataColumns.InstalledApp.VERSION_NAME:
installedVersionName = cursor.getString(i); installedVersionName = cursor.getString(i);
break; break;
case AppProvider.DataColumns.InstalledApp.SIGNATURE:
installedSig = cursor.getString(i);
break;
case "_id": case "_id":
break; break;
default: default:

View File

@ -209,6 +209,7 @@ public class AppProvider extends FDroidProvider {
interface InstalledApp { interface InstalledApp {
String VERSION_CODE = "installedVersionCode"; String VERSION_CODE = "installedVersionCode";
String VERSION_NAME = "installedVersionName"; String VERSION_NAME = "installedVersionName";
String SIGNATURE = "installedSig";
} }
String[] ALL = { String[] ALL = {
@ -220,6 +221,7 @@ public class AppProvider extends FDroidProvider {
IGNORE_THISUPDATE, ICON_URL, ICON_URL_LARGE, IGNORE_THISUPDATE, ICON_URL, ICON_URL_LARGE,
SUGGESTED_VERSION_CODE, SuggestedApk.VERSION, SUGGESTED_VERSION_CODE, SuggestedApk.VERSION,
InstalledApp.VERSION_CODE, InstalledApp.VERSION_NAME, InstalledApp.VERSION_CODE, InstalledApp.VERSION_NAME,
InstalledApp.SIGNATURE,
}; };
} }
@ -354,6 +356,9 @@ public class AppProvider extends FDroidProvider {
case DataColumns.InstalledApp.VERSION_CODE: case DataColumns.InstalledApp.VERSION_CODE:
addInstalledAppVersionCode(); addInstalledAppVersionCode();
break; break;
case DataColumns.InstalledApp.SIGNATURE:
addInstalledSig();
break;
case DataColumns._COUNT: case DataColumns._COUNT:
appendCountField(); appendCountField();
break; break;
@ -402,6 +407,13 @@ public class AppProvider extends FDroidProvider {
); );
} }
private void addInstalledSig() {
addInstalledAppField(
InstalledAppProvider.DataColumns.SIGNATURE,
DataColumns.InstalledApp.SIGNATURE
);
}
private void addInstalledAppField(String fieldName, String alias) { private void addInstalledAppField(String fieldName, String alias) {
leftJoinToInstalledTable(); leftJoinToInstalledTable();
appendField(fieldName, "installed", alias); appendField(fieldName, "installed", alias);

View File

@ -97,10 +97,12 @@ public class DBHelper extends SQLiteOpenHelper {
+ InstalledAppProvider.DataColumns.APP_ID + " TEXT NOT NULL PRIMARY KEY, " + InstalledAppProvider.DataColumns.APP_ID + " TEXT NOT NULL PRIMARY KEY, "
+ InstalledAppProvider.DataColumns.VERSION_CODE + " INT NOT NULL, " + InstalledAppProvider.DataColumns.VERSION_CODE + " INT NOT NULL, "
+ InstalledAppProvider.DataColumns.VERSION_NAME + " TEXT NOT NULL, " + InstalledAppProvider.DataColumns.VERSION_NAME + " TEXT NOT NULL, "
+ InstalledAppProvider.DataColumns.APPLICATION_LABEL + " TEXT NOT NULL " + InstalledAppProvider.DataColumns.APPLICATION_LABEL + " TEXT NOT NULL, "
+ InstalledAppProvider.DataColumns.SIGNATURE + " TEXT NOT NULL "
+ " );"; + " );";
private static final String DROP_TABLE_INSTALLED_APP = "DROP TABLE " + TABLE_INSTALLED_APP + ";";
private static final int DB_VERSION = 50; private static final int DB_VERSION = 51;
private final Context context; private final Context context;
@ -278,11 +280,11 @@ public class DBHelper extends SQLiteOpenHelper {
renameRepoId(db, oldVersion); renameRepoId(db, oldVersion);
populateRepoNames(db, oldVersion); populateRepoNames(db, oldVersion);
if (oldVersion < 43) createInstalledApp(db); if (oldVersion < 43) createInstalledApp(db);
addAppLabelToInstalledCache(db, oldVersion);
addIsSwapToRepo(db, oldVersion); addIsSwapToRepo(db, oldVersion);
addChangelogToApp(db, oldVersion); addChangelogToApp(db, oldVersion);
addIconUrlLargeToApp(db, oldVersion); addIconUrlLargeToApp(db, oldVersion);
updateIconUrlLarge(db, oldVersion); updateIconUrlLarge(db, oldVersion);
recreateInstalledCache(db, oldVersion);
} }
/** /**
@ -478,13 +480,11 @@ public class DBHelper extends SQLiteOpenHelper {
db.execSQL(CREATE_TABLE_INSTALLED_APP); db.execSQL(CREATE_TABLE_INSTALLED_APP);
} }
private void addAppLabelToInstalledCache(SQLiteDatabase db, int oldVersion) { // If any column was added or removed, just drop the table, create it
if (oldVersion < 45) { // again and let the cache be filled from scratch again.
Utils.debugLog(TAG, "Adding applicationLabel to installed app table. " + private void recreateInstalledCache(SQLiteDatabase db, int oldVersion) {
"Turns out we will need to repopulate the cache after doing this, " + if (oldVersion < 51) {
"so just dropping and recreating the table (instead of altering and adding a column). " + db.execSQL(DROP_TABLE_INSTALLED_APP);
"This will force the entire cache to be rebuilt, including app names.");
db.execSQL("DROP TABLE fdroid_installedApp;");
createInstalledApp(db); createInstalledApp(db);
} }
} }

View File

@ -4,6 +4,7 @@ import android.content.ContentProviderOperation;
import android.content.Context; import android.content.Context;
import android.content.OperationApplicationException; import android.content.OperationApplicationException;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.RemoteException; import android.os.RemoteException;
@ -110,7 +111,8 @@ public class InstalledAppCacheUpdater {
Map<String, Integer> cachedInfo = InstalledAppProvider.Helper.all(context); Map<String, Integer> cachedInfo = InstalledAppProvider.Helper.all(context);
List<PackageInfo> installedPackages = context.getPackageManager().getInstalledPackages(0); List<PackageInfo> installedPackages = context.getPackageManager()
.getInstalledPackages(PackageManager.GET_SIGNATURES);
for (PackageInfo appInfo : installedPackages) { for (PackageInfo appInfo : installedPackages) {
toInsert.add(appInfo); toInsert.add(appInfo);
if (cachedInfo.containsKey(appInfo.packageName)) { if (cachedInfo.containsKey(appInfo.packageName)) {
@ -137,6 +139,8 @@ public class InstalledAppCacheUpdater {
.withValue(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName) .withValue(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName)
.withValue(InstalledAppProvider.DataColumns.APPLICATION_LABEL, .withValue(InstalledAppProvider.DataColumns.APPLICATION_LABEL,
InstalledAppProvider.getApplicationLabel(context, info.packageName)) InstalledAppProvider.getApplicationLabel(context, info.packageName))
.withValue(InstalledAppProvider.DataColumns.SIGNATURE,
InstalledAppProvider.getPackageSig(info))
.build(); .build();
ops.add(op); ops.add(op);
} }

View File

@ -4,15 +4,19 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.UriMatcher; import android.content.UriMatcher;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Hasher;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -59,9 +63,11 @@ public class InstalledAppProvider extends FDroidProvider {
String VERSION_CODE = "versionCode"; String VERSION_CODE = "versionCode";
String VERSION_NAME = "versionName"; String VERSION_NAME = "versionName";
String APPLICATION_LABEL = "applicationLabel"; String APPLICATION_LABEL = "applicationLabel";
String SIGNATURE = "sig";
String[] ALL = { String[] ALL = {
_ID, APP_ID, VERSION_CODE, VERSION_NAME, APPLICATION_LABEL, _ID, APP_ID, VERSION_CODE, VERSION_NAME, APPLICATION_LABEL,
SIGNATURE,
}; };
} }
@ -106,6 +112,18 @@ public class InstalledAppProvider extends FDroidProvider {
return packageName; // all else fails, return id return packageName; // all else fails, return id
} }
public static String getPackageSig(PackageInfo info) {
Signature sig = info.signatures[0];
String sigHash = "";
try {
Hasher hash = new Hasher("MD5", sig.toCharsString().getBytes());
sigHash = hash.getHash();
} catch (NoSuchAlgorithmException e) {
// ignore
}
return sigHash;
}
@Override @Override
protected String getTableName() { protected String getTableName() {
return DBHelper.TABLE_INSTALLED_APP; return DBHelper.TABLE_INSTALLED_APP;

View File

@ -57,6 +57,8 @@ public class PackageAddedReceiver extends PackageReceiver {
values.put(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName); values.put(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName);
values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL, values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL,
InstalledAppProvider.getApplicationLabel(context, appId)); InstalledAppProvider.getApplicationLabel(context, appId));
values.put(InstalledAppProvider.DataColumns.SIGNATURE,
InstalledAppProvider.getPackageSig(info));
context.getContentResolver().insert(uri, values); context.getContentResolver().insert(uri, values);
} }

View File

@ -40,7 +40,7 @@ abstract class PackageReceiver extends BroadcastReceiver {
protected PackageInfo getPackageInfo(Context context, String appId) { protected PackageInfo getPackageInfo(Context context, String appId) {
PackageInfo info = null; PackageInfo info = null;
try { try {
info = context.getPackageManager().getPackageInfo(appId, 0); info = context.getPackageManager().getPackageInfo(appId, PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
// ignore // ignore
} }

View File

@ -59,6 +59,8 @@ public class PackageUpgradedReceiver extends PackageReceiver {
values.put(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName); values.put(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName);
values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL, values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL,
InstalledAppProvider.getApplicationLabel(context, appId)); InstalledAppProvider.getApplicationLabel(context, appId));
values.put(InstalledAppProvider.DataColumns.SIGNATURE,
InstalledAppProvider.getPackageSig(info));
context.getContentResolver().insert(uri, values); context.getContentResolver().insert(uri, values);
} }