app.uninstallable is only used in AppDetails. It is only set when
generating App instances from installed APKs for the swap stuff. Since it
is initialized to false and used as !app.uninstallable, it is always true
when used. So it was doing nothing.
This needs to be thought out more so this is not entirely complete for
#628. AppDetails needs to know whether its a system app to provide proper
feedback and swap needs to know whether its a system app with an update
installed, otherwise it should ignore it.
Decent Java editors have all sorts of nice ways to show javadoc comments,
whether they are for public or private APIs. So comments should be in that
format whenever possible.
To generate swap's index.jar, lots of information about all the installed
APKs needs to be parsed. That can take a long time. Some of that can be
stored in InstalledAppProvider. This prepares for those changes.
Also, turns out that packageInfo.applicationInfo provides enough info, so
there is no need to use pm.getApplicationInfo(). And the metadata from
GET_META_DATA was not even being used.
The previous logic was putting the header on some 4" screens while not
putting it on a 7" tablet. Tested with:
* Samsung Galaxy Tab 3 7"
* Azpen A727 7"
* Xiaomi 4.5"
* Lenovo 4"
* emulators...
Use initLoader instead of resetartLoader. Call onResume after setting category.
While working on !311, I had some logging in place in the `AppProvider`. I ended up straying into an investigation of issue #606 and noticed with my logging that we have been doing more queries than required for some time, because the loader was being forceably recreated when it could instead be reused in onResume.
Perhaps more importantly, we were querying twice for list of apps to display. The first time is "Give me all the apps" because we haven't set the category yet. Then we set the category, which causes us to ask for "Apps matching this category". The fix for this is for the `onResume` method (which results in a cursor being created) to be called _after_ setting the selected category is set. This ensures that the query is run has the correct category the first time, and needn't be run again.
I am not confident that this fixes the issue in #606, but I haven't seen it reproduced since I've implemented this fix.
See merge request !312
While investigating the infamous issue #606, I noticed these two things which
were a little off. Firstly, we were doing more queries than required, because the
loader was being forceably recreated when it could instead be reused in onResume.
Also, the onResume method (which results in a cursor beign created) should be
called _after_ setting the selected category. This ensures that the query
which is run has the correct category the first time, and needn't be run again.
I am not confident that this fixes the issue, but it seems to help, and I believe
it is the correct thing to do even if it doesn't fix 606.
My guess is that is from IPv6, but those should be filtered out in this
code before it gets to the crash point. Here's the stacktrace:
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.IllegalArgumentException: Value [64] not in range [0,32]
at org.apache.commons.net.util.SubnetUtils.rangeCheck(SubnetUtils.java:339)
at org.apache.commons.net.util.SubnetUtils.calculate(SubnetUtils.java:264)
at org.apache.commons.net.util.SubnetUtils.<init>(SubnetUtils.java:51)
at org.fdroid.fdroid.net.WifiStateChangeService.setIpInfoFromNetworkInterface(WifiStateChangeService.java:222)
at org.fdroid.fdroid.net.WifiStateChangeService.access$300(WifiStateChangeService.java:37)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:99)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:71)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
... 4 more
java.lang.IllegalArgumentException: Value [64] not in range [0,32]
at org.apache.commons.net.util.SubnetUtils.rangeCheck(SubnetUtils.java:339)
at org.apache.commons.net.util.SubnetUtils.calculate(SubnetUtils.java:264)
at org.apache.commons.net.util.SubnetUtils.<init>(SubnetUtils.java:51)
at org.fdroid.fdroid.net.WifiStateChangeService.setIpInfoFromNetworkInterface(WifiStateChangeService.java:222)
at org.fdroid.fdroid.net.WifiStateChangeService.access$300(WifiStateChangeService.java:37)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:99)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:71)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
This is very related to #660 but this time, I can't see any other way to
solve it but a null guard. I don't think it is possible to guarantee that
the Downloader.ACTION_INTERRUPTED receiver will be unregistered since
onDestroy() might not even be called.
java.lang.NullPointerException
at org.fdroid.fdroid.installer.InstallManagerService.removeFromActive(InstallManagerService.java:328)
at org.fdroid.fdroid.installer.InstallManagerService.access$400(InstallManagerService.java:58)
at org.fdroid.fdroid.installer.InstallManagerService$4.onReceive(InstallManagerService.java:212)
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:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5353)
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:830)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:646)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
at dalvik.system.NativeStart.main(Native Method)
android.bluetooth.BluetoothSocket.isConnected() is only 14+
java.lang.NoSuchMethodError: android.bluetooth.BluetoothSocket.isConnected
at org.fdroid.fdroid.net.bluetooth.BluetoothConnection.open(BluetoothConnection.java:37)
at org.fdroid.fdroid.net.bluetooth.BluetoothClient.openConnection(BluetoothClient.java:31)
at org.fdroid.fdroid.net.BluetoothDownloader.<init>(BluetoothDownloader.java:30)
at org.fdroid.fdroid.net.DownloaderFactory.create(DownloaderFactory.java:56)
at org.fdroid.fdroid.RepoUpdater.downloadIndex(RepoUpdater.java:97)
at org.fdroid.fdroid.RepoUpdater.update(RepoUpdater.java:131)
at org.fdroid.fdroid.UpdateService.onHandleIntent(UpdateService.java:377)
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)
When building APK instances from installed apps, the minSdkVersion and
maxSdkVersion needs to be parsed directly from the APK's Android Manifest,
since PackageManager does not provide a method to get it how we need it.
Previously, the whole AndroidManifest.xml file was parsed entirely twice,
once for minSdkVersion then for maxSdkVersion.
The original logic had maxSdkVersion=0 meaning infinity. That was changed
to be a very large value SDK_VERSION_MAX_VALUE, but getMinMaxSdkVersion()
was still returning 0 for APKs where maxSdkVersion was not set.
This is a follow up on fc0df0dcf4dd0d5f13de82d7cd9254b2b48cb62d
Apparently, the CREATOR field is not (yet?) needed in the tests, since
they work without it. This gets us closer to making lint errors fail
the CI builds.
closes#580
notification update improvements
Ok, I made a mistake in c35d327fa4c503425c629d49778c001b08ecb833 / !301 that messed up installing. Turns out it was a simple fix: 0838a15792fd70afde4e3743296ce88c961a299e This also removes the _Cancel_ button from the one active download notification to provide a working, albeit verbose, notification UX for 0.100.
After this commit, these commits and the ones from !301 should be included in `stable-0.100` and a new alpha tagged :)
See merge request !306
This is the recommended way to deal with updating Notifications. Each new
update should be a new Notification, but the same Builder instance should
be used to generate each new Notification instance.
New installs where being caught up in the logic to check whether a download
is still in progress after InstallManagerService got killed. Also checking
whether Intent was just redelivered lets the new installs through while
screening out the inactive Intents that were redelivered. This logic also
cancels the notification for any download that was in progress when the
InstallManagerService was killed.
#660
Check repo index timestamps
The Update Framework documents provide a [nice discussion of possible attacks](https://github.com/theupdateframework/tuf/blob/develop/SECURITY.md) against update systems. One example is a "rollback attack", where the attacker just serves the old signed `index.jar` to keep all clients from updating their apps. That allows the attacker to exploit known vulnerabilities in those un-updated apps.
While this is a reasonably hard attack, this fix is an important step towards removing the requirement for trusting the web server operator. Ultimately, it should be able trusting the index signing key only. Then it doesn't matter were the files come from, it just matters that they are verifiably signed by the index signing key.
This does not address "freeze attacks" since it allows an index update with the same timestamp. I did that deliberately to slowly ramp up the security checks in order to avoid problems along the way. Code-wise, blocking freeze-attacks is mostly a matter of changing the timestamp check from `<` to `<=`.
See merge request !302
Couple more 0.100 fixes
This is a couple more bug fixes towards finally releasing 0.100. These should be cherry-picked into `stable-0.100`.
See merge request !301
Installer instances always copy the APK to a safe place to run the install
from. That copy needs to be deleted. Until we have the whole lifecycle in
InstallManagerService, we need this hack. It should be handled on the
broadcast from InstallerService to say that its complete.
#611!300
Including the App and Apk instances in the Intent that starts
InstallManagerService ensures that the needed data is present in the
Service no matter what happens outside of the Service. For example, if the
index is updated or cleared while an install is in progress, the install
process still needs to know the name and packageName of the app to update
the Notification.
A cleaner but more labor-intensive way to implement this would be to make
App and Apk properly implement the full Parcelable interface. That would
require tests to check that the Parcelable methods have all the same fields
as toContentValues() and the database.
closes#660https://gitlab.com/fdroid/fdroidclient/issues/660