use of org.fdroid.fdroid.data.Apk in project fdroidclient by f-droid.
the class ApkVerifierTest method testExtendedPerms.
@Test
public void testExtendedPerms() throws IOException, ApkVerifier.ApkPermissionUnequalException, ApkVerifier.ApkVerificationException {
RepoDetails actualDetails = getFromFile(extendedPermsXml);
HashSet<String> expectedSet = new HashSet<>(Arrays.asList("android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_WIFI_STATE", "android.permission.INTERNET", "android.permission.READ_SYNC_STATS", "android.permission.READ_SYNC_SETTINGS", "android.permission.WRITE_SYNC_SETTINGS", "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", "android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS", "android.permission.READ_CALENDAR", "android.permission.WRITE_CALENDAR"));
if (Build.VERSION.SDK_INT <= 18) {
expectedSet.add("android.permission.READ_EXTERNAL_STORAGE");
expectedSet.add("android.permission.WRITE_EXTERNAL_STORAGE");
}
if (Build.VERSION.SDK_INT <= 22) {
expectedSet.add("android.permission.GET_ACCOUNTS");
expectedSet.add("android.permission.AUTHENTICATE_ACCOUNTS");
expectedSet.add("android.permission.MANAGE_ACCOUNTS");
}
if (Build.VERSION.SDK_INT >= 23) {
expectedSet.add("android.permission.CAMERA");
if (Build.VERSION.SDK_INT <= 23) {
expectedSet.add("android.permission.CALL_PHONE");
}
}
Apk apk = actualDetails.apks.get(0);
HashSet<String> actualSet = new HashSet<>(Arrays.asList(apk.requestedPermissions));
for (String permission : expectedSet) {
if (!actualSet.contains(permission)) {
Log.i(TAG, permission + " in expected but not actual! (android-" + Build.VERSION.SDK_INT + ")");
}
}
for (String permission : actualSet) {
if (!expectedSet.contains(permission)) {
Log.i(TAG, permission + " in actual but not expected! (android-" + Build.VERSION.SDK_INT + ")");
}
}
String[] expectedPermissions = expectedSet.toArray(new String[expectedSet.size()]);
assertTrue(ApkVerifier.requestedPermissionsEqual(expectedPermissions, apk.requestedPermissions));
String[] badPermissions = Arrays.copyOf(expectedPermissions, expectedPermissions.length + 1);
assertFalse(ApkVerifier.requestedPermissionsEqual(badPermissions, apk.requestedPermissions));
badPermissions[badPermissions.length - 1] = "notarealpermission";
assertFalse(ApkVerifier.requestedPermissionsEqual(badPermissions, apk.requestedPermissions));
Uri uri = Uri.fromFile(extendedPermissionsApk);
ApkVerifier apkVerifier = new ApkVerifier(instrumentation.getContext(), uri, apk);
apkVerifier.verifyApk();
}
use of org.fdroid.fdroid.data.Apk in project fdroidclient by f-droid.
the class ApkVerifierTest method testImpliedPerms.
@Test
public void testImpliedPerms() throws IOException {
RepoDetails actualDetails = getFromFile(extendedPermsXml);
TreeSet<String> expectedSet = new TreeSet<>(Arrays.asList("android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_WIFI_STATE", "android.permission.INTERNET", "android.permission.READ_CALENDAR", "android.permission.READ_CONTACTS", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.READ_SYNC_SETTINGS", "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", "android.permission.WRITE_CALENDAR", "android.permission.WRITE_CONTACTS", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.WRITE_SYNC_SETTINGS", "org.dmfs.permission.READ_TASKS", "org.dmfs.permission.WRITE_TASKS"));
if (Build.VERSION.SDK_INT <= 22) {
// maxSdkVersion="22"
expectedSet.addAll(Arrays.asList("android.permission.AUTHENTICATE_ACCOUNTS", "android.permission.GET_ACCOUNTS", "android.permission.MANAGE_ACCOUNTS"));
}
if (Build.VERSION.SDK_INT >= 29) {
expectedSet.add("android.permission.ACCESS_MEDIA_LOCATION");
}
Apk apk = actualDetails.apks.get(1);
Log.i(TAG, "APK: " + apk.apkName);
HashSet<String> actualSet = new HashSet<>(Arrays.asList(apk.requestedPermissions));
for (String permission : expectedSet) {
if (!actualSet.contains(permission)) {
Log.i(TAG, permission + " in expected but not actual! (android-" + Build.VERSION.SDK_INT + ")");
}
}
for (String permission : actualSet) {
if (!expectedSet.contains(permission)) {
Log.i(TAG, permission + " in actual but not expected! (android-" + Build.VERSION.SDK_INT + ")");
}
}
String[] expectedPermissions = expectedSet.toArray(new String[expectedSet.size()]);
assertTrue(ApkVerifier.requestedPermissionsEqual(expectedPermissions, apk.requestedPermissions));
expectedSet = new TreeSet<>(Arrays.asList("android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_WIFI_STATE", "android.permission.AUTHENTICATE_ACCOUNTS", "android.permission.GET_ACCOUNTS", "android.permission.INTERNET", "android.permission.MANAGE_ACCOUNTS", "android.permission.READ_CALENDAR", "android.permission.READ_CONTACTS", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.READ_SYNC_SETTINGS", "android.permission.WRITE_CALENDAR", "android.permission.WRITE_CONTACTS", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.WRITE_SYNC_SETTINGS", "org.dmfs.permission.READ_TASKS", "org.dmfs.permission.WRITE_TASKS"));
if (Build.VERSION.SDK_INT >= 29) {
expectedSet.add("android.permission.ACCESS_MEDIA_LOCATION");
}
expectedPermissions = expectedSet.toArray(new String[expectedSet.size()]);
apk = actualDetails.apks.get(2);
Log.i(TAG, "APK: " + apk.apkName);
actualSet = new HashSet<>(Arrays.asList(apk.requestedPermissions));
for (String permission : expectedSet) {
if (!actualSet.contains(permission)) {
Log.i(TAG, permission + " in expected but not actual! (android-" + Build.VERSION.SDK_INT + ")");
}
}
for (String permission : actualSet) {
if (!expectedSet.contains(permission)) {
Log.i(TAG, permission + " in actual but not expected! (android-" + Build.VERSION.SDK_INT + ")");
}
}
assertTrue(ApkVerifier.requestedPermissionsEqual(expectedPermissions, apk.requestedPermissions));
}
use of org.fdroid.fdroid.data.Apk in project fdroidclient by f-droid.
the class FileInstallerTest method testInstallOtaZip.
@Test
public void testInstallOtaZip() {
Apk apk = new Apk();
apk.apkName = "org.fdroid.fdroid.privileged.ota_2010.zip";
apk.packageName = "org.fdroid.fdroid.privileged.ota";
apk.versionCode = 2010;
assertFalse(apk.isApk());
Installer installer = InstallerFactory.create(context, apk);
assertEquals("should be a FileInstaller", FileInstaller.class, installer.getClass());
}
use of org.fdroid.fdroid.data.Apk in project fdroidclient by f-droid.
the class AntiFeaturesTest method antiFeaturesSaveCorrectly.
@Test
public void antiFeaturesSaveCorrectly() {
List<Apk> notVulnApks = ApkProvider.Helper.findByPackageName(context, notVuln.packageName);
assertEquals(3, notVulnApks.size());
List<Apk> allVulnApks = ApkProvider.Helper.findByPackageName(context, allVuln.packageName);
assertEquals(3, allVulnApks.size());
for (Apk apk : allVulnApks) {
assertArrayEquals(new String[] { "KnownVuln", "ContainsGreenButtons" }, apk.antiFeatures);
}
List<Apk> vulnAtV2Apks = ApkProvider.Helper.findByPackageName(context, vulnAtV2.packageName);
assertEquals(3, vulnAtV2Apks.size());
for (Apk apk : vulnAtV2Apks) {
if (apk.versionCode == 2) {
assertArrayEquals(new String[] { "KnownVuln", "ContainsGreenButtons" }, apk.antiFeatures);
} else {
assertNull(apk.antiFeatures);
}
}
}
use of org.fdroid.fdroid.data.Apk in project fdroidclient by f-droid.
the class IndexV1Updater method processIndexV1.
/**
* Parses the index and feeds it to the database via {@link Repo}, {@link App},
* and {@link Apk} instances. This uses {@link RepoPersister} to add the apps
* and packages to the database in {@link RepoPersister#saveToDb(App, List)}
* to write the {@link Repo}, and commit the whole thing in
* {@link RepoPersister#commit(ContentValues, long)}. One confusing thing about this
* whole process is that {@link RepoPersister} needs to first create and entry
* in the database, then fetch the ID from the database to populate
* {@link Repo#id}. That has to happen first, then the rest of the {@code Repo}
* data must be added later.
*
* @param indexInputStream {@link InputStream} to {@code index-v1.json}
* @param etag the {@code etag} value from HTTP headers
* @throws IOException
* @throws UpdateException
*/
public void processIndexV1(InputStream indexInputStream, JarEntry indexEntry, String etag) throws IOException, UpdateException {
Utils.Profiler profiler = new Utils.Profiler(TAG);
profiler.log("Starting to process index-v1.json");
ObjectMapper mapper = getObjectMapperInstance(repo.getId());
JsonFactory f = mapper.getFactory();
JsonParser parser = f.createParser(indexInputStream);
HashMap<String, Object> repoMap = null;
App[] apps = null;
Map<String, String[]> requests = null;
Map<String, List<Apk>> packages = null;
// go into the main object block
parser.nextToken();
while (true) {
String fieldName = parser.nextFieldName();
if (fieldName == null) {
break;
}
switch(fieldName) {
case "repo":
repoMap = parseRepo(mapper, parser);
break;
case "requests":
requests = parseRequests(mapper, parser);
break;
case "apps":
apps = parseApps(mapper, parser);
break;
case "packages":
packages = parsePackages(mapper, parser);
break;
}
}
// ensure resources get cleaned up timely and properly
parser.close();
profiler.log("Finished processing index-v1.json. Now verifying certificate...");
if (repoMap == null) {
return;
}
long timestamp = (Long) repoMap.get("timestamp") / 1000;
if (repo.timestamp > timestamp) {
throw new IndexUpdater.UpdateException(repo, "index.jar is older that current index! " + timestamp + " < " + repo.timestamp);
}
X509Certificate certificate = getSigningCertFromJar(indexEntry);
verifySigningCertificate(certificate);
profiler.log("Certificate verified. Now saving to database...");
// timestamp is absolutely required
repo.timestamp = timestamp;
// below are optional, can be null
repo.lastetag = etag;
repo.name = getStringRepoValue(repoMap, "name");
repo.icon = getStringRepoValue(repoMap, "icon");
repo.description = getStringRepoValue(repoMap, "description");
// ensure the canonical URL is included in the "mirrors" list as the first entry
LinkedHashSet<String> mirrors = new LinkedHashSet<>();
mirrors.add(repo.address);
mirrors.addAll(getStringListRepoValue(repoMap, "mirrors"));
repo.mirrors = mirrors.toArray(new String[mirrors.size()]);
// below are optional, can be default value
repo.maxage = getIntRepoValue(repoMap, "maxage");
repo.version = getIntRepoValue(repoMap, "version");
if (TextUtils.isEmpty(platformSigCache)) {
PackageInfo androidPackageInfo = Utils.getPackageInfoWithSignatures(context, "android");
platformSigCache = Utils.getPackageSig(androidPackageInfo);
}
RepoPersister repoPersister = new RepoPersister(context, repo);
if (apps != null && apps.length > 0) {
int appCount = 0;
for (App app : apps) {
appCount++;
List<Apk> apks = null;
if (packages != null) {
apks = packages.get(app.packageName);
}
if (apks == null) {
Log.i(TAG, "processIndexV1 empty packages");
apks = new ArrayList<>(0);
}
if (apks.size() > 0) {
app.preferredSigner = apks.get(0).sig;
app.isApk = true;
for (Apk apk : apks) {
if (!apk.isApk()) {
app.isApk = false;
} else if (apk.sig.equals(platformSigCache)) {
app.preferredSigner = platformSigCache;
}
}
}
if (appCount % 50 == 0) {
notifyProcessingApps(appCount, apps.length);
}
repoPersister.saveToDb(app, apks);
}
}
profiler.log("Saved to database, but only a temporary table. Now persisting to database...");
notifyCommittingToDb();
ContentValues contentValues = new ContentValues();
contentValues.put(Schema.RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), ""));
contentValues.put(Schema.RepoTable.Cols.TIMESTAMP, repo.timestamp);
contentValues.put(Schema.RepoTable.Cols.LAST_ETAG, repo.lastetag);
if (repo.version != Repo.INT_UNSET_VALUE) {
contentValues.put(Schema.RepoTable.Cols.VERSION, repo.version);
}
if (repo.maxage != Repo.INT_UNSET_VALUE) {
contentValues.put(Schema.RepoTable.Cols.MAX_AGE, repo.maxage);
}
if (repo.description != null) {
contentValues.put(Schema.RepoTable.Cols.DESCRIPTION, repo.description);
}
if (repo.name != null) {
contentValues.put(Schema.RepoTable.Cols.NAME, repo.name);
}
if (repo.icon != null) {
contentValues.put(Schema.RepoTable.Cols.ICON, repo.icon);
}
if (repo.mirrors != null && repo.mirrors.length > 0) {
contentValues.put(Schema.RepoTable.Cols.MIRRORS, Utils.serializeCommaSeparatedString(repo.mirrors));
}
repoPersister.commit(contentValues, repo.getId());
profiler.log("Persisted to database.");
if (repo.pushRequests == Repo.PUSH_REQUEST_ACCEPT_ALWAYS) {
processRepoPushRequests(requests);
Utils.debugLog(TAG, "Completed Repo Push Requests: " + requests);
}
}
Aggregations