Provide content Uris to downloaded apks via FileProvider
* moves apk verification back inside the Installer class
* uses support libs FileProvider for content Uris
* move apk file caching and storage methods into ApkFileProvider class
Some of the ugly version checks for Android N can be removed after Android N has been released. Unfortunately Google decided to keep SDK version at 23 for Android N dev preview and only change the CODENAME, thus ``Build.VERSION.SDK_INT <= Build.VERSION_CODES.M`` returns true on Android N preview :/ , see https://commonsware.com/blog/2016/03/17/backwards-compatibility-n-developer-preview.html
Tested on Android N dev preview 3 emulator, Android 6 stock and Android 5.1 rooted with priv extension.
See merge request !331
The two helper methods alleviate the need for copious null checks. They also provide
consistent behaviour when there are zero elements (i.e. they return null, rather than
an empty string or empty array, as was the case before).
This is a combination of:
* `String[].split(",")` and
* `TextUtils.join(",", values)`
It seems a bit wastefull to have our own implementation of these two things
which lightly wrap this code, and produce a datastructure which is non standard
and foreign to Java developers.
* moves apk verification back inside the Installer class
* uses support libs FileProvider for content Uris
* move apk file caching and storage methods into
ApkCache class
We missed an off-by-one in my previous DB change:
90467bf8bf2f8e4a46cb1db563154df4035bf746
This causes the installed app parsing to happen on each start when on any
build that is on db-version/56. Its not a big deal since the broken code
was not shipped at all, even in an alpha.
In order to avoid having null guards making the code ugly, use a "blank"
instance of Apk which will work for the various comparisons. This fixes
this crash:
fixes#688
java.lang.NullPointerException
at org.fdroid.fdroid.installer.InstallManagerService$4.onReceive(InstallManagerService.java:243)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
Improve order by/selection logic for database layer.
This was extracted from the postponed !311.
The order by stuff previously only allowed specifying a particular field. We should also be able to sort based on arbitrary expressions, and such expressions will require the ability to bind arguments using the "?" syntax. This change provides a Java abstraction for the order by, and improves the handling of selection arguments that need to bind to "?" so that both the selection (i.e. `WHERE` clause) and the `ORDER BY` clause can provide arguments as required.
See merge request !329
A previous commit accidentally pushed the code which queries the
`PackageManager` to a different method, but then still used the
`packageInfo` which was supposed to be populated by that code.
This change rectifies this, and in the process also clarifies/documents under
what circumstances the `PackageManager` needs to be queried, rather than
relying on the incoming intent.
Fixes#686.
The order by stuff previously only allowed specifying a particular field.
We should also be able to sort based on arbitrary expressions, and such
expressions will require the ability to bind arguments using the "?" syntax.
This changes provides a Java abstraction for the order by, and improves
the handling of selection arguments that need to bind to "?" so that both
the selection (i.e. WHERE clause) and the ORDER BY clause can provide
arguments as required.
It is not a particularly expensive operation in the scheme of things. When we
are going to the database, the bottlneck is in disk access for the actual query
of the database tables. The difference between retrieving two columns or
all the columns when the query is for a handful of apps is inconsequential.
Thus, it is better to be safe than sorry and just ask for all the things so
that our value objects are correctly populated.
Permissions UI in AppDetails
* Removes the "m" prefix and some unnecessary TODOs
* Re-uses the permission list from the privileged installer for the list in AppDetails:

See merge request !332
include targetSdkVersion in Apk
In order to work well with the Android 6.0+ permissions, the client needs to know whether an APK has been built against android-23 or higher.
@pserwylo @dschuermann how does this look?
See merge request !323
Check permissions for unattended installer
This PR introduces the class ``ApkVerifier`` which checks the permissions of the downloaded apk file against the expected permissions from the F-Droid listing (``Apk`` class).
* I removed ``AndroidXMLDecompress`` because everything which it has been used for can also be done with ``PackageManager.getPackageArchiveInfo()``, to the best of my knowledge. I even asked in at a similar project why ``PackageManager.getPackageArchiveInfo()``may not be enough: https://github.com/jaredrummler/APKParser/issues/3 It turns out in our case it should do everything we need.
* The code responsible for sanitizing the local apk file and making it world readable has also been moved into ``ApkVerifier`` for now. This can change in a later PR when I introduce the FileProvider for downloaded apks.
We still need to check the target sdk version (see TODO in ``ApkVerifier``). This depends on https://gitlab.com/fdroid/fdroidclient/merge_requests/323
See merge request !322
Remove first-time dialogs for extension installer
The root mechanism will not be useful in 5.1 and later and has been shown to be error prone.
See merge request !330
The tests pass, but there is a lingering message that gets logged:
```
Jun 08, 2016 7:31:13 AM com.almworks.sqlite4java.Internal log
WARNING: [sqlite] [DETACH DATABASE temp_update_db]DB[1][C]: exception when clearing
com.almworks.sqlite4java.SQLiteException: [1] DB[1] reset [no such database: temp_update_db]
at com.almworks.sqlite4java.SQLiteConnection.throwResult(SQLiteConnection.java:1309)
at com.almworks.sqlite4java.SQLiteConnection.throwResult(SQLiteConnection.java:1282)
at com.almworks.sqlite4java.SQLiteConnection.cacheStatementHandle(SQLiteConnection.java:1211)
at com.almworks.sqlite4java.SQLiteConnection.access$900(SQLiteConnection.java:54)
at com.almworks.sqlite4java.SQLiteConnection$CachedController.dispose(SQLiteConnection.java:1606)
at com.almworks.sqlite4java.SQLiteStatement.dispose(SQLiteStatement.java:187)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$4.call(ShadowSQLiteConnection.java:421)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:449)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:443)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
```
The `temp_update_db` is the one used for repo updates, but I thought that it
correctly gets dropped/detached by the `TempAppProvider` when required. In fact,
given the nature of the error message (no such database: temp_update_db), that
hints at the fact that it is indeed dropped. I'm struggling to figure out what
causes this, but it should not be harmful to the running of the tests. If a test
actually fails, then it is picked up correctly by JUnit.
This makes testing of the function easier, as the method previously expected
a real file to exist on disk for which it could then hash. This instead allows
mock hash values to be inserted when under test.
Other than this, the semantics remain exactly the same as before, and the
expensive hashing is still done on a worker thread as part of the `IntentService`.
Service crash fixes
3 relatively simple crash fixes. The two related to `WifiStateChangeService` have already be included in `stable-0.100`, 7385d320b42af960be63c9c179e1cbf186c1398a should be cherry-picked into `stable-0.100` after this is merged.
I already have 198ad843c1fabc8cf57ffe85c77230288cd6d7a4 ready in my stable-0.100 branch
See merge request !317
Historically the providers were responsible for notifying about inserts/deletes
for this table. However this is no longer the case with the new service responsible
for throttling the rate with which these notifications occur.
The `parseApp` method was previously accepting an `Intent`, which could
have been anything. Given it was only used once, this now pushed the
creation of that `Intent` into the `parseApp` method, and also reduced the
visibility of the method as it is only used once at time of writing.
This should not be a particularly expensive opperation,. Also, at time of
writing it is only used in a background thread, and only used once in that
thread (i.e. not in a loop or anything like that).
Now that we have RX as a dependency, it can be used as a nice concise way to
achieve certain tasks. Rate limiting is one thing it does well - via the
`debounce` mechanism:
http://reactivex.io/documentation/operators/debounce.html
The semantics of this code is the same as before, limiting content change notifications
to one per second.
caught java.lang.StringIndexOutOfBoundsException:
at java.lang.String.substring(String.java:1651)
at java.lang.String.subSequence(String.java:2040)
at org.fdroid.fdroid.data.App.setFromPackageInfo(App.java:298)
at org.fdroid.fdroid.data.App.<init>(App.java:268)
at org.fdroid.fdroid.localrepo.CacheSwapAppsService.onHandleIntent(CacheSwapAppsService.java:78)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:59)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.os.HandlerThread.run(HandlerThread.java:60)
This adds a check of whether the database has the current APK in it, based
on PackageInfo's lastUpdateTime field. This avoids recalculating the hash
of the whole APK, which is quite time and resource intensive.
The APK hash is useful for comparing whether something is exactly the same
file as something else. For example, to compare whether the installed APK
matches something that f-droid.org hosts. The "last update time" is a fast
way to check whether the information is current.
InstallAppProviderService now processes install and delete events one at a
time, where InstalledAppCacheUpdater made a batch of changes which it ran
all at once. This means that InstallAppProviderService will send out a
flood of notifications when first initializing, since it will index every
single installed app and send a notification for each one. This makes the
GUI lock up. This commit puts a rate limit on those notifications if they
start coming fast. They are limited to one per second.
InstalledAppCacheUpdater was a custom Service-like thing with some
threading issues. InstalledAppProviderService is an IntentService that
relies on the built-in queue and threading of the IntentService to make
sure that things are processed nicely in the background and one at a time.
This changes the announcing so that each app added/changed/deleted triggers
a new annoucement. This keeps the UI more updated, and makes the Installed
tab show something as soon as possible, rather than waiting for the all of
the install apps to be processed. This becomes more important as more
stuff is added to InstalledAppProvider, like the hash of the APK.
This also strips down and simplifies the related BroadcastReceivers.
BroadcastReceivers work on the UI thread, so they should do as little work
as possible. PackageManagerReceiver just rebadges the incoming Intent and
sends it off to InstalledAppProviderService for processing.
The SwapService is the central container for all things swap. If anything
at all related to swap is active, then SwapService needs to be running.
That also means that stopping SwapService should stop all things related to
swapping, including any screens or notifications.
fixes#258https://gitlab.com/fdroid/fdroidclient/issues/258
Since it takes a chunk of time to generate and write the app index.jar when
swapping apps, this service starts running in the background immediately
when SwapService starts. It first indexes the installed apps that were not
cached, then caches apps based PACKAGE_ADDED broadcasts. It does not index
system apps, since there are many and they are rarely swapped.
If the install process is interrupted, then InstallManagerService is no
longer managing it. It will make the announcements and set the
notification, then forget about that APK.