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.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@ -96,7 +95,6 @@ import org.fdroid.fdroid.net.AsyncDownloaderFromAndroid;
import org.fdroid.fdroid.net.Downloader;
import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.List;
@ -104,8 +102,6 @@ interface AppDetailsData {
App getApp();
AppDetails.ApkListAdapter getApks();
String getInstalledSigHash();
}
/**
@ -188,8 +184,8 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
return getString(R.string.app_not_installed);
}
// Definitely installed this version.
if (mInstalledSigID != null && apk.sig != null
&& apk.sig.equals(mInstalledSigID)) {
if (app.installedSig != null && apk.sig != null
&& apk.sig.equals(app.installedSig)) {
return getString(R.string.app_installed);
}
// 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
protected void onStart() {
super.onStart();
@ -601,7 +594,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
* like that) and then finish the activity.
*/
private void setApp(App newApp) {
if (newApp == null) {
Toast.makeText(this, R.string.no_such_app, Toast.LENGTH_LONG).show();
finish();
@ -612,21 +604,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
startingIgnoreAll = app.ignoreAllUpdates;
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() {
@ -853,8 +830,8 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
alert.show();
return;
}
if (mInstalledSigID != null && apk.sig != null
&& !apk.sig.equals(mInstalledSigID)) {
if (app.installedSig != null && apk.sig != null
&& !apk.sig.equals(app.installedSig)) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.SignatureMismatch).setPositiveButton(
R.string.ok,
@ -1044,11 +1021,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
return adapter;
}
@Override
public String getInstalledSigHash() {
return mInstalledSigID;
}
public static class AppDetailsSummaryFragment extends Fragment {
protected final Preferences prefs;
@ -1092,10 +1064,6 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
return data.getApks();
}
protected String getInstalledSigHash() {
return data.getInstalledSigHash();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
@ -1408,11 +1376,11 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
return;
}
App app = getApp();
TextView signatureView = (TextView) view.findViewById(R.id.signature);
String sig = getInstalledSigHash();
if (prefs.expertMode() && !TextUtils.isEmpty(sig)) {
if (prefs.expertMode() && !TextUtils.isEmpty(app.installedSig)) {
signatureView.setVisibility(View.VISIBLE);
signatureView.setText("Signed: " + sig);
signatureView.setText("Signed: " + app.installedSig);
} else {
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 String installedSig;
public boolean system;
public boolean updatedSystemApp;
@ -209,6 +211,9 @@ public class App extends ValueObject implements Comparable<App> {
case AppProvider.DataColumns.InstalledApp.VERSION_NAME:
installedVersionName = cursor.getString(i);
break;
case AppProvider.DataColumns.InstalledApp.SIGNATURE:
installedSig = cursor.getString(i);
break;
case "_id":
break;
default:

View File

@ -209,6 +209,7 @@ public class AppProvider extends FDroidProvider {
interface InstalledApp {
String VERSION_CODE = "installedVersionCode";
String VERSION_NAME = "installedVersionName";
String SIGNATURE = "installedSig";
}
String[] ALL = {
@ -220,6 +221,7 @@ public class AppProvider extends FDroidProvider {
IGNORE_THISUPDATE, ICON_URL, ICON_URL_LARGE,
SUGGESTED_VERSION_CODE, SuggestedApk.VERSION,
InstalledApp.VERSION_CODE, InstalledApp.VERSION_NAME,
InstalledApp.SIGNATURE,
};
}
@ -354,6 +356,9 @@ public class AppProvider extends FDroidProvider {
case DataColumns.InstalledApp.VERSION_CODE:
addInstalledAppVersionCode();
break;
case DataColumns.InstalledApp.SIGNATURE:
addInstalledSig();
break;
case DataColumns._COUNT:
appendCountField();
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) {
leftJoinToInstalledTable();
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.VERSION_CODE + " INT 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;
@ -278,11 +280,11 @@ public class DBHelper extends SQLiteOpenHelper {
renameRepoId(db, oldVersion);
populateRepoNames(db, oldVersion);
if (oldVersion < 43) createInstalledApp(db);
addAppLabelToInstalledCache(db, oldVersion);
addIsSwapToRepo(db, oldVersion);
addChangelogToApp(db, oldVersion);
addIconUrlLargeToApp(db, oldVersion);
updateIconUrlLarge(db, oldVersion);
recreateInstalledCache(db, oldVersion);
}
/**
@ -478,13 +480,11 @@ public class DBHelper extends SQLiteOpenHelper {
db.execSQL(CREATE_TABLE_INSTALLED_APP);
}
private void addAppLabelToInstalledCache(SQLiteDatabase db, int oldVersion) {
if (oldVersion < 45) {
Utils.debugLog(TAG, "Adding applicationLabel to installed app table. " +
"Turns out we will need to repopulate the cache after doing this, " +
"so just dropping and recreating the table (instead of altering and adding a column). " +
"This will force the entire cache to be rebuilt, including app names.");
db.execSQL("DROP TABLE fdroid_installedApp;");
// If any column was added or removed, just drop the table, create it
// again and let the cache be filled from scratch again.
private void recreateInstalledCache(SQLiteDatabase db, int oldVersion) {
if (oldVersion < 51) {
db.execSQL(DROP_TABLE_INSTALLED_APP);
createInstalledApp(db);
}
}

View File

@ -4,6 +4,7 @@ import android.content.ContentProviderOperation;
import android.content.Context;
import android.content.OperationApplicationException;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.RemoteException;
@ -110,7 +111,8 @@ public class InstalledAppCacheUpdater {
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) {
toInsert.add(appInfo);
if (cachedInfo.containsKey(appInfo.packageName)) {
@ -137,6 +139,8 @@ public class InstalledAppCacheUpdater {
.withValue(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName)
.withValue(InstalledAppProvider.DataColumns.APPLICATION_LABEL,
InstalledAppProvider.getApplicationLabel(context, info.packageName))
.withValue(InstalledAppProvider.DataColumns.SIGNATURE,
InstalledAppProvider.getPackageSig(info))
.build();
ops.add(op);
}

View File

@ -4,15 +4,19 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Hasher;
import org.fdroid.fdroid.Utils;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
@ -59,9 +63,11 @@ public class InstalledAppProvider extends FDroidProvider {
String VERSION_CODE = "versionCode";
String VERSION_NAME = "versionName";
String APPLICATION_LABEL = "applicationLabel";
String SIGNATURE = "sig";
String[] ALL = {
_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
}
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
protected String getTableName() {
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.APPLICATION_LABEL,
InstalledAppProvider.getApplicationLabel(context, appId));
values.put(InstalledAppProvider.DataColumns.SIGNATURE,
InstalledAppProvider.getPackageSig(info));
context.getContentResolver().insert(uri, values);
}

View File

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

View File

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