This is hopefully a bit more efficient for large log files than the previous
single TextView. The ListView widget also provides an auto-scroll mechanism.
Always reset the error state when disconnecting via state service. This
way the error state is also cleared when the connection is terminated
directly via control activity.
For instance, rotating a device will restart it and this previously
could have started the wrong profile or shown the system's VPN
confirmation dialog twice.
As documented, onActivityResult() is called right before onResume() when
the activity is reactivated. However, if the system's VPN confirmation
dialog is shown and the home button is pressed, the activity is stopped
and not just paused, so its state is saved. And onActivityResult() is
actually also called before onStart(). This means that no fragment
transactions may be committed (i.e. no dialog may be shown) when the
activity is later restarted (e.g. because there is another attempt to
connect the VPN) until onStart() has been called. So if we'd try to show
the error dialog in onActivityResult() after returning to the launcher
it would result in an IllegalStateException.
However, showing the dialog for the previous confirmation dialog is not
ideal anyway, so we just ignore that result.
This allows cancelling connecting if e.g. the OCSP server is not
reachable. Previously this caused some delay in disconnecting state but
even worse it cause an ANR if the user tried reconnecting during that
time as the main thread would get struck in setNextProfile() (we could
probably find a better solution there too in the future).
It's reinstalled when reconnecting (or during error recovery) and
eventually uninstalled after disconnecting.
Only on Android 5+, otherwise we'd block our fetcher (and Android 4.4 is
stupid in regards to overlapping TUN devices anyway).
Note that Android 8's blocking feature blocks everything that passes by
the VPN, so this only works when tunneling everything (i.e. neither subnets,
nor apps can be excluded from the VPN if that feature is enabled).
Otherwise, a blocking VPN interface would prevent our fetcher from working
as we currently rely on an interface that doesn't allow access to the
underlying socket/FD, which would be required to call VpnService.protect().
Removing and readding the entry to a potentially different row/segment,
while driving out waiting and new threads, could prevent threads from
acquiring the SA even if they were waiting to check it out by unique
ID (which doesn't change), or if they were just trying to enumerate it.
With this change the row and segment doesn't change anymore and waiting
threads may acquire the SA. However, those looking for an IKE_SA by SPIs
might get one back that has a different SPI (but that's probably not
something that happens very often this early).
This was noticed because we check out SAs by unique ID in the Android
app to terminate them after failed retransmits if we are not reestablishing
the SA (otherwise we continue), and this sometimes failed.
Fixes: eaedcf8c00 ("ike-sa-manager: Add method to change the initiator SPI of an IKE_SA")
The button to view the log is now below the status info. And since the
IMC results are just below that we don't need a special handling for
that anymore.
This can happen on systems (e.g. Android 7.x) where Always-on VPNs are
triggered right after booting before the KeyChain is unlocked by the user.
Retrieving the certificate chain or private key then fails with
"KeyChainException: IllegalStateException: keystore is LOCKED" until the
user unlocks the screen once.
The built-in client actually also fails in this situation (e.g. with XAuth
RSA), it tries three times then stops and shows an error notification.
With Android 8.1 this isn't triggered after a reboot until the device
has been unlocked once (solving the issue with the key store) and traffic
may optionally be blocked by the user until the VPN is established.
There are still some issues (e.g. password prompts and fatal errors), and we
might need some workaround for older Android releases.
Only if there is no currently active (or previously active) profile does
this currently operate on the configured (or stored most recently used)
profile. This way it's possible to use a different connection and
quickly disable and re-enable it again. When unlocked the profile name
is shown, when locked a generic text is used (this detection doesn't seem
to work 100% reliably). To disconnect, the user is forced to unlock the
device, connecting is possible without, if the credentials are available
and no fatal error occurs (it even works with the system credential store,
at least on Android 8.1).
Note that the tile is not available right after a reboot. It seems that
the system has to be unlocked once to activate third-party tiles (will
be interesting to see how this works together with Always-on VPN).