Privacy Browser

Saved Instance State

Beginning with Privacy Browser 3.5, the app now saves and restores the instance state if killed in the background by the OS.

For those who might not be aware of the abomination that is memory management on Android, I would point you to the picture below, which was taken from the official Android Lifecycle documentation.

They make sure to let you know that this is a simplified representation.

Notice that nice App process killed item on the left. Turns out that Android likes to do this all the time.

Those of us who grew up using desktops have a general expectation that programs keep running until we close them. To work around this on Android, apps have the option to save their instance state and recreate it when onCreate() is run again. This creates the impression that the app has been running continuously without such actually having been the case.

Needless to say, there are a lot of possible pieces of information that would need to be saved to recreate the exact state of an app after it has been restarted. Previous to 3.5, Privacy Browser did not attempt to do this at all, but with the new Day/Night theme, it became a high priority to do so, because the app restarts whenever the theme changes.

There are three data storage methods that can be used to store this data: ViewModels, saved instance states, and persistent storage. These are described in the saving UI states documentation. The important detail for our purposes is that ViewModels only exist in RAM, saved instance states exist in RAM, but can be written to the flash storage if the OS needs to recover RAM for other purposes, and persistent storage always exists on the flash storage. The bookmarks and domain settings databases are examples of persistent storage used by Privacy Browser.

One of the primary design philosophies of Privacy Browser is to minimize the amount of data that is stored on the device. Specifically, just browsing to a website should not create a record of where you have been. There are far to many examples of tricky ways this information can be exfiltrated from a device to compromise a user’s privacy. As such, I really wanted to handle all data storage using ViewModels. Unfortunately, because of limitations in the type of data that ViewModels can store, Privacy Browser also has to use a saved instance state to correctly reinitialize after being restarted by the OS.

The following pieces of sensitive information are stored in the saved instance state:

  • The list of all the currently open tabs, including each URL.
  • The WebView history for each tab, including the URL of each item in the history.

Depending on what happens, this information can be written to a system area of the flash storage. I consider this to be suboptimal, but there is no way to prevent it from happening if Privacy Browser is to function well on Android.

The good news is that running Clear and Exit from the navigation menu or by tapping the X to close the last tab deletes the saved instance state. Those who have significant privacy needs can enable Incognito Mode, which wipes the WebView history every time a new website is loaded.

Privacy Browser

Privacy Browser 3.5.1

Privacy Browser 3.5.1 contains two important bug fixes relating to the the new Day/Night theme and how Privacy Browser now handles being restarted by the OS.

The first fixes a problem with the intents (links from other apps) not opening if Privacy Browser was restarted in the background by the OS. Privacy Browser would restore the previously open tabs but not load the new link.

The second fixes a problem with Privacy Browser not initializing correctly if the theme app theme was set to be the opposite of the OS theme. This would manifest differently on different devices, but would generally prevent bookmarks or even some links from loading on the first tab.

Privacy Browser

Privacy Browser 3.5

Privacy Browser 3.5 has been released. It switches to using Android’s relatively new Day/Night theme, which constitutes the largest UI change since the original Privacy Browser was released. This fixes a longstanding bug with alert dialogs in the dark theme, makes the app font colors easier on the eye in dark mode, and fights burn-in.

Beyond the styling of the app itself, the dark WebView theme has also been switched from using a JavaScript CSS override, to using WebView’s relatively new built-in dark mode. There are more details about how this works on the updated Dark Theme page. But the big takeaway is that JavaScript no longer needs to be enabled for the dark mode to work. In most cases, the resulting dark websites look better than they did before.

Privacy Browser now saves and restores its state if it is killed in the background by the OS. This is necessary to make switching between the Day and Night themes work correctly, but it also has benefits in a number of other scenarios. However, there are negative privacy implications of saving the instance state, which are discussed in dept in their own blog post.

Privacy Browser is now smarter about proposing names for file downloads.


Mojeek has been added to the list of search engines, and Qwant and Searx have been removed. The requirements for having a search engine listed are as follows:

  1. The search engine must work.
  2. The search engine must use HTTPS encryption.
  3. The search engine must function with JavaScript disabled.

Some search engines use different URLs when JavaScript is disabled and enabled. In those cases, as long as the JavaScript disabled version works well, I have included both options for those who do chose to enable JavaScript either globally or through a domain setting.

I don’t like any of them.

File uploading has been fixed for some sites. The disabling of swipe-to-refresh has been fixed in some scenarios. Custom headers are now applied when loading a link from the WebView and not just from the URL bar. And the context menu items have been reordered for consistency and priority.

All the translated strings inside the app were updated for this release except for Turkish. Due to the temporary unavailability of the French and Italian translators when the release was being prepared, the changelogs in those languages were not translated in time to be included, but they will be added in the future.

The next release will focus on cleaning up a bunch of little bugs and feature requests.

Privacy Browser

Privacy Browser Will Never Monetize the Default Search Engine

This morning I received the following email.

Hi Soren,

I hope all is well. I’m Chris – CMO at Startpage and I came across your Privacy Browser as I was looking into F-Droid.

I saw Startpage on the screenshots on
I assume you’re a fan and wanted to reach out to hear what you think of Startpage and how it is currently integrated in Privacy Browser.
If possible I would like to help you in monetizing the searches coming to Startpage from Privacy Browser.

I look forward hearing from you.

All the best, Chris

Christiaan Solcer

Privacy Browser will never monetize the default search engine for the following two reasons.

If the search engine is monetized, decisions about the search engine are no longer made in the best interest of the users

I currently select the list of search engines included in the browser, and the default search engine and homepage, based on what I consider to be best for users. You can read the rational for switching to Startpage as part of the 3.2 release. As noted in that post, there are things I don’t like about Startpage, and it is likely I will switch to something else in the future if I can find something better.

But if I formed a financial relationship with Startpage, that would unduly influence any decision to switch to a different default search engine in the future. Instead of making the decision based on the best interests of the users, it would be made on which search engine offered the most money.

Once you monetize the search engine, there is a huge incentive to not block privacy invasions

Mozilla makes almost all their money by monetizing the default search engine in Firefox. In 2018 they made $435 million. Do you think search engines would be willing to pay them that much money if they couldn’t track what individual users are searching for? For example, if I responded to the email above expressing interest in receiving money for using Startpage as the default search engine, do you think they would be OK with me continuing to block their trackers?

Naughty, naughty.

My personal belief is that the primary reason why Mozilla only makes token attempts to protect user privacy is because they are financially tied to allowing the default search engine to track users. For example, they do not disable JavaScript by default. They have an exceptionally lose Referrer Policy. They don’t integrate an ad blocker into default installs.

This corrupt relationship between search engines and browsers is the primary reason why I started development of Privacy Browser. There is no chance that I will ever monetize the default search engine, no matter how much money they offer.

Financial Reports Privacy Browser

Liberapay Account

F-Droid turned me on to Liberapay, which is similar to Patreon, but with a focus on open source principles. As such, it is right up my alley.

I have created a Liberapay account and listed it on the Donations page.

Privacy Browser

New WordPress 2020 Theme

The theme for the website has been updated from WordPress Twenty Nineteen to Twenty Twenty. Overall I like the effect. The only downside I have seen so far is that the menu doesn’t work if JavaScript is disabled. Someday, if I have some free time, I might contribute to the WordPress project to fix that.

Privacy Browser

Mastodon Account

A while back a user suggested I setup a Mastodon account for Privacy Browser, but at the time I didn’t feel that I needed anything in addition to all the existing communication platforms I was using. But, as is typically the case, over time my perceptions have changed, and I thought it would be nice to have a platform where I could toot about Privacy Browser’s development as I work on features between releases. I expect I will use it similar to the blog posts I write after each release, but more focused on giving insights to each feature I am developing as things are shaping up for a new release.

The account is registered at

Privacy Browser

Privacy Browser 3.4.1

Privacy Browser 3.4.1 has been released. It contains an emergency fix for some Android 10 devices that couldn’t access the public directories even when the storage permission was granted. This caused denied errors when the storage permission was granted and the default download location was used on these devices.

Privacy Browser

Privacy Browser 3.4

Privacy Browser 3.4 has been released. It replaces the use of Android’s built-in download manager with a custom implementation. I didn’t start off trying to do this. Rather, I was building a feature to save a raw URL, and after I had done it I realized I could use it for all download purposes.

As I explain in the design guidelines, I attempt to reuse existing Android elements as much as possible. However, there are a couple of things about Android’s download manager that are unsatisfactory. First, it doesn’t provide a mechanism to specify a proxy. Second, it doesn’t work at all on Android 7.0 when a VPN is enabled. This was the reason that Privacy Browser added an option to download with an external program in version 2.14. With this release, that is no longer needed, so it has been removed.

Look at all that beautiful information.

There are a few things I really like about this implementation. First, the download URL is editable live. This is a feature that isn’t available on any other browser I have used, that, once I realized I could do it, tickled me pink. It isn’t something I would use often, but it doesn’t take any more space than displaying the URL, which is what I was originally going to do, and there are a few times I would like to modify a URL before download.

The file size is retrieved via an HTTP HEAD request whenever the URL is edited. This also indicates if a URL is invalid.

Not even Chuck Norris could download that URL.

The file name is populated using the new download location option combined with a file name extracted from the URL.

This was feature request 32. It is nice to finally get it done.

File locations in Android are complex, so they deserve a bit of explanation. The first aspect is that the naming structure has changed over time. In older version of Android, user files were located in /storage/sdcard. Beginning in Android Lollipop (version 5.0, API 21), the file location was changed to /storeage/emulated/0. The 0 indicates the first profile. If there are other profiles on the phone, they will have different numbers. Note that it is possible that OEM customizations to Android might change these locations.

In addition to the base location, beginning with Android Marshmallow (version 6.0, API 23) an apps ability to read and write files is restricted based on the storage permission. So, any app can read and write to its public directory, which is a subdirectory that includes the app’s ID. For the standard flavor of Privacy Browser it is /storage/emulated/0/Android/data/com.stoutner.privacybrowser.standard. Any files saved in this directory are deleted by Android if the app is uninstalled. Reading and writing to other public directories requires the storage permission.

Selecting auto for the download location automatically chooses an appropriate directory based on the version of Android and the status of the storage permission. For example, on my phone it selects /storage/emulated/0/Download if the storage permission has been granted and /storage/emulated/0/Android/data/com.stoutner.privacybrowser.standard/files if it hasn’t.

If custom is selected, the user can use the separate Download Custom Location preference to specify a location. It should be a user-writable path that doesn’t end in a /. If a user attempts to download a file to a public directory without enabling the storage permission a notice is displayed at the bottom of the save dialog. Before the file is saved, a dialog is presented requesting that the user grant the storage permission.

Amazingly, an app can check to see if a file exists in a directory where it neither has read nor write access!

As can be seen in the screenshots above, the save dialog now warns if a download will overwrite an existing file. Not only does the save dialog appear when a file is downloaded, but it can also be launched from the context and options menus.

When files have finished there is an option to open them displayed in a snackbar. Except for APK files on Android Oreo (version 8.1, APK 26) or newer, where the REQUEST_INSTALL_PACKAGES permission is required to install an APK.

Tabs are now reloaded when the proxy changes. I added a Bookmarks entry to the options menu so that the bookmarks drawer can be opened on devices using Android 10’s gesture navigation. And the options menu has been reordered to prevent accidental creation of domain settings.

The poor options menu rarely makes it through a release without a modification.

All the translations were updated with this release except for Turkish, which is currently looking for a translator. Under the hood, this release marks the beginning of the migration of the codebase from Java to Kotlin. The About View Source dialog was migrated in this release. Migrations should increase in future releases.

The next version of Privacy Browser will integrate with Android 10’s new day/night theme.

Privacy Browser


I received an email asking about implementing DNS over HTTPS (DoH) in Privacy Browser. As this is a fairly complex topic, and as there are likely to be other people with the same question, I thought it best to answer with this blog post.

First, a little background about the problem. When DNS (the Domain Name System) was originally designed, people hadn’t really thought through the security and privacy concerns that exist with the internet as we have it today. As such, the original protocol was designed to use UDP (on port 53). This has implications for DDoS amplification attacks, but more importantly for our purposes, it means that DNS traffic is not encrypted. Every time you make a request to view a website in a web browser, your device uses DNS to lookup the IP address of the server. Anyone who is able to listen to the packets leaving your system, like your ISP or the government of the country where you live, can read those DNS requests and, because they are not encrypted, can tell which websites you are visiting.

DoH is designed to address this problem by encrypting DNS traffic and sending it over the HTTPS protocol (which uses TCP instead of UDP and is encrypted). There has been a fair amount of discussion recently about DoH because both Firefox and Chrome have added experimental options to enable it in the browser. This sparked a bit of privacy theater in Europe, and has generally raised the consciousness of users about this topic. The purpose of this post is to explain what DoH is, why it isn’t the best solution to the problem, and why I don’t believe that DNS requests should be handled at the browser level.

  1. I am a big fan of encrypting everything that moves across the internet. I think running DNS over UDP is a poor idea. Most DNS servers now have the option of running a separate TCP interface, but it still often isn’t the default for either servers or clients. Moving to TCP, even unencrypted TCP (still on port 53), removes the problem of the DDoS amplification attacks, although at the cost of making DNS requests take a little more bandwidth and a little more time.
  2. But once you switch everything to use TCP for DNS, it is easy to encrypt that DNS traffic. This is known as DNS over TCP (DoT, which runs on port 853). This is the solution that everyone should be aiming for instead of DoH. While DoT is a little more bandwidth hungry than DNS over UDP, DoH is hugely more bandwidth hungry and much slower. This is because DoH must first setup a TCP connection, then it must setup a encrypted TCP session, then it must setup an entire HTTPS session (huge and bulky and overly complex) just to make a DNS request. So, the first part of the answer is that the solution we should be going for is DoT instead of DoH. Beginning with Android 9 Pie, the OS uses DoT by default as long as it is supported by the DNS server.
  3. Proponents of DoH point to scenarios where some type of ISP or government firewall blocks DoT traffic as a reason to use DoH (which runs on port 443 like normal HTTPS traffic). This is because DoH traffic looks a lot more like normal HTTPS traffic than DoT, so the argument is that it would be harder to block. While it is true that it would be marginally harder to block, it wouldn’t be that difficult to fingerprint it, because it doesn’t actually look that much like normal web browsing traffic. And the IP addresses DoH traffic is heading to are known DNS servers, so it would be fairly east to blacklist those IP addresses if one wanted to. However, the easiest solution to this problem is for all DoT servers to also run a DoH instance, and clients can downgrade from DoT to DoH instead of DNS over UDP if they cannot make a DoT connection.
  4. But this leads us to the next point, which is that even if all DNS traffic were encrypted with DoT or DoH, it would still be laughably easy for ISPs or governments to know exactly what websites you are visiting, so nothing has been gained from a privacy standpoint. This is why I call it privacy theater. Let me enumerate how they can do this in points 5-7 below.
  5. When your device makes an HTTPS request to a server for a website, it includes the server name in plaintext at the beginning of the encrypted packet. Let me slow down and say that again: every packet you send to a HTTPS server includes the unencrypted domain name of the server you want to speak with. This is called Server Name Indication (SNI). SNI exists to solve a very real problem: most servers host multiple domains on the same IP address. The server needs to know which certificate to use when it receives the first packet from your device. If it picks the wrong certificate, your device will show an SSL certificate error. So the packet contains the domain in plaintext to facilitate this. Anyone who could read your unencrypted DNS traffic can also read the SNI on your encrypted HTTPS traffic. So, until the SNI problem is fixed, encrypting DNS is meaningless from a privacy standpoint.
  6. There is some effort being made to encrypt SNI, which is included as an optional component of TLS 1.3. But implementing it is complicated and breaks things. I don’t see anything like this going mainstream until it is implemented as a core part of the TLS protocol and not as an optional component. Which means that we won’t see this until TLS 1.4 at the earliest. And it also means that it won’t work in the real world until the lowest protocol supported by either the server or the client is TLS 1.4, because otherwise the person who it attempting to sniff the domain name could just force a TLS downgrade to 1.3 to get the information. So, at a minimum I put that at 10 years.
  7. At face value the argument would then be that we should just get rid of SNI and have a 1:1 correlation between domain names and IP addresses. But this would make it even easier for ISPs and governments to track your web traffic, because there are companies who maintain lists of all the domain names on the internet and the IP addresses they are linked with. 95% of the websites you visit can be identified just by the list of IP addresses they use. Without SNI, that would be 100%. And the IP address, like the destination address on a letter, is one part of a packet that can’t be encrypted because otherwise the routers on the internet wouldn’t know where to send it.
  8. By now it should be apparent that DoT is the solution we are looking for to encrypt DNS, and that from a privacy standpoint it won’t mean anything until we also get encrypted SNI (and that even after both are encrypted it won’t mean very much). Which leads us to the question: what is Privacy Browser going to do about it. The answer is that Privacy Browser is going to do nothing. The reason I say that is not to be flippant, but because DNS is something I feel very strongly should not be dealt with at the browser level. It should be handled entirely by the OS. I have written a bit about this topic in relation to minimizing Privacy Browser’s attack surface. The idea is that, from a security perspective, the browser shouldn’t try to recreate core OS functions, like encryption or DNS. Not only does this introduce another attack surface that is unlikely to be as well audited as the functionality in the core OS, but it also overrides core OS networking settings at an app level, which leads to unexpected behavior, like not updating DNS servers to match the current network or VPN settings.
  9. This is the part of the conversation where we explain how DoH can actually decrease your privacy instead of merely doing nothing to increase it. During the normal course of the day, you probably connect to multiple DNS servers. When you wake up you probably use the DNS server provided by your home ISP (available over your home Wi-Fi). When you are commuting to work or school, you use your cell phone company’s DNS server. When you arrive at work or school, you connect to the DNS server provided through their Wi-Fi. Each of these DNS servers has a partial list of the the websites you visit throughout the day, but it would be hard to stitch them into a cohesive whole. However, if you use DoH as it is currently implemented in Chrome and Firefox, you would use one DNS server all the time. The two biggest servers that support DoH are run by Google and Cloudflare. Cloudflare promises they won’t do anything bad with that information. But you have no way of verifying that such is the case. And Google, they don’t even make any promises. There is a lot of additional information about this topic on a blog entitled Centralized DoH is bad for privacy, in 2019 and beyond.
  10. To round out this conversation, we should talk a little bit about DNSSEC (Domain Name System Security Extensions). The whole purpose of DoH is to encrypt traffic in transit while it is going between a DNS server and your device. The actual DNS data on the server is unencrypted, and, as a huge security problem, can be modified by that server before it is sent to you. DNSSEC digitally signs DNS information, so that only the owner of a DNS record can make changes. This verifies that the information DNS servers give you is what it is supposed to be. Because it is quite complex to implement, and because it dramatically increases the size of DNS responses, DNSSEC still hasn’t caught on as the default configuration in most cases. But we hopefully are a little closer than encrypted SNI.

Each of the points above represents almost a gross oversimplification of the complexities of these issues. And, because I am constantly learning more about these issues, the implication is that I do not yet know everything about them. As such, I am perfectly willing to change my mind about any of the above topics if I am presented with a persuasive argument to the contrary. I labeled each section above so that is it easy to reference them by number in the comments if you feel that there is anything I have missed.