VmTerminalApp: Implement Wi-Fi only checkbox in installer
Bug: 379218935
Test: Manually on CF as follows: \
- No network --> network error or no Wi-Fi error \
- Switch to another Wi-Fi --> network error \
- Turn on/off Wi-Fi when Wi-Fi only --> no Wi-Fi error \
- Turn on/off Wi-Fi when not Wi-Fi only --> download continues
Change-Id: I6be2a95f3b1a37218a1115bb333c335cedd3d5c4
diff --git a/android/TerminalApp/aidl/com/android/virtualization/terminal/IInstallerService.aidl b/android/TerminalApp/aidl/com/android/virtualization/terminal/IInstallerService.aidl
index daf1fa4..1ae1951 100644
--- a/android/TerminalApp/aidl/com/android/virtualization/terminal/IInstallerService.aidl
+++ b/android/TerminalApp/aidl/com/android/virtualization/terminal/IInstallerService.aidl
@@ -19,7 +19,7 @@
import com.android.virtualization.terminal.IInstallProgressListener;
interface IInstallerService {
- void requestInstall();
+ void requestInstall(boolean isWifiOnly);
void setProgressListener(in IInstallProgressListener listener);
boolean isInstalling();
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
index 83c6b4c..db42b9f 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
@@ -169,7 +169,7 @@
if (mService != null) {
try {
- mService.requestInstall();
+ mService.requestInstall(mWaitForWifiCheckbox.isChecked());
} catch (RemoteException e) {
handleCriticalError(e);
}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
index f97f16f..e0923ee 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
@@ -21,11 +21,15 @@
import android.app.Service;
import android.content.Intent;
import android.content.pm.ServiceInfo;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.os.Build;
import android.os.IBinder;
import android.os.SELinux;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.GuardedBy;
@@ -38,7 +42,9 @@
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.ref.WeakReference;
+import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.file.Files;
@@ -70,9 +76,14 @@
private boolean mIsInstalling;
@GuardedBy("mLock")
+ private boolean mHasWifi;
+
+ @GuardedBy("mLock")
private IInstallProgressListener mListener;
private ExecutorService mExecutorService;
+ private ConnectivityManager mConnectivityManager;
+ private MyNetworkCallback mNetworkCallback;
@Override
public void onCreate() {
@@ -92,6 +103,18 @@
.build();
mExecutorService = Executors.newSingleThreadExecutor();
+
+ mConnectivityManager = getSystemService(ConnectivityManager.class);
+ Network defaultNetwork = mConnectivityManager.getBoundNetworkForProcess();
+ if (defaultNetwork != null) {
+ NetworkCapabilities capability =
+ mConnectivityManager.getNetworkCapabilities(defaultNetwork);
+ if (capability != null) {
+ mHasWifi = capability.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ }
+ }
+ mNetworkCallback = new MyNetworkCallback();
+ mConnectivityManager.registerDefaultNetworkCallback(mNetworkCallback);
}
@Nullable
@@ -117,9 +140,10 @@
if (mExecutorService != null) {
mExecutorService.shutdown();
}
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
}
- private void requestInstall() {
+ private void requestInstall(boolean isWifiOnly) {
synchronized (mLock) {
if (mIsInstalling) {
Log.i(TAG, "already installing..");
@@ -138,8 +162,7 @@
mExecutorService.execute(
() -> {
- // TODO(b/374015561): Provide progress update
- boolean success = downloadFromSdcard() || downloadFromUrl();
+ boolean success = downloadFromSdcard() || downloadFromUrl(isWifiOnly);
if (success) {
reLabelImagesSELinuxContext();
}
@@ -188,15 +211,31 @@
return false;
}
+ private boolean checkForWifiOnly(boolean isWifiOnly) {
+ if (!isWifiOnly) {
+ return true;
+ }
+ synchronized (mLock) {
+ return mHasWifi;
+ }
+ }
+
// TODO(b/374015561): Support pause/resume download
- // TODO(b/374015561): Wait for Wi-Fi on metered network if requested.
- private boolean downloadFromUrl() {
+ private boolean downloadFromUrl(boolean isWifiOnly) {
Log.i(TAG, "trying to download from " + IMAGE_URL);
+ if (!checkForWifiOnly(isWifiOnly)) {
+ Log.e(TAG, "Install isn't started because Wifi isn't available");
+ notifyError(getString(R.string.installer_error_no_wifi));
+ return false;
+ }
+
try (BufferedInputStream inputStream =
new BufferedInputStream(new URL(IMAGE_URL).openStream());
+ WifiCheckInputStream wifiInputStream =
+ new WifiCheckInputStream(inputStream, isWifiOnly);
TarArchiveInputStream tar =
- new TarArchiveInputStream(new GzipCompressorInputStream(inputStream))) {
+ new TarArchiveInputStream(new GzipCompressorInputStream(wifiInputStream))) {
ArchiveEntry entry;
Path baseDir = InstallUtils.getInternalStorageDir(this).toPath();
Files.createDirectories(baseDir);
@@ -208,20 +247,22 @@
Files.copy(tar, extractTo, StandardCopyOption.REPLACE_EXISTING);
}
}
- } catch (UnknownHostException e) {
+ } catch (WifiCheckInputStream.NoWifiException e) {
+ Log.e(TAG, "Install failed because of Wi-Fi is gone");
+ notifyError(getString(R.string.installer_error_no_wifi));
+ return false;
+ } catch (UnknownHostException | SocketException e) {
// Log.e() doesn't print stack trace for UnknownHostException
- Log.e(TAG, "Install failed UnknownHostException: " + e.getMessage());
- notifyError(getString(R.string.installer_install_network_error_message));
+ Log.e(TAG, "Install failed: " + e.getMessage(), e);
+ notifyError(getString(R.string.installer_error_network));
return false;
} catch (IOException e) {
- // TODO(b/374015561): Provide more finer grained error message
Log.e(TAG, "Installation failed", e);
notifyError(getString(R.string.installer_error_unknown));
return false;
}
if (!InstallUtils.resolvePathInVmConfig(this)) {
- // TODO(b/374015561): Provide more finer grained error message
notifyError(getString(R.string.installer_error_unknown));
return false;
}
@@ -272,10 +313,10 @@
}
@Override
- public void requestInstall() {
+ public void requestInstall(boolean isWifiOnly) {
InstallerService service = ensureServiceConnected();
synchronized (service.mLock) {
- service.requestInstall();
+ service.requestInstall(isWifiOnly);
}
}
@@ -303,4 +344,57 @@
}
}
}
+
+ private final class WifiCheckInputStream extends InputStream {
+ private static final int READ_BYTES = 1024;
+
+ private final InputStream mInputStream;
+ private final boolean mIsWifiOnly;
+
+ public WifiCheckInputStream(InputStream is, boolean isWifiOnly) {
+ super();
+ mInputStream = is;
+ mIsWifiOnly = isWifiOnly;
+ }
+
+ @Override
+ public int read(byte[] buf, int offset, int numToRead) throws IOException {
+ int totalRead = 0;
+ while (numToRead > 0) {
+ if (!checkForWifiOnly(mIsWifiOnly)) {
+ throw new NoWifiException();
+ }
+ int read =
+ mInputStream.read(buf, offset + totalRead, Math.min(READ_BYTES, numToRead));
+ if (read <= 0) {
+ break;
+ }
+ totalRead += read;
+ numToRead -= read;
+ }
+ return totalRead;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (!checkForWifiOnly(mIsWifiOnly)) {
+ throw new NoWifiException();
+ }
+ return mInputStream.read();
+ }
+
+ private static final class NoWifiException extends SocketException {
+ // empty
+ }
+ }
+
+ private final class MyNetworkCallback extends ConnectivityManager.NetworkCallback {
+ @Override
+ public void onCapabilitiesChanged(
+ @NonNull Network network, @NonNull NetworkCapabilities capability) {
+ synchronized (mLock) {
+ mHasWifi = capability.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ }
+ }
+ }
}
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index 300cbbc..d498286 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -38,6 +38,8 @@
<string name="installer_notif_desc_text">Linux terminal will be started after finish</string>
<!-- Toast error message for install failure due to the network issue [CHAR LIMIT=none] -->
<string name="installer_error_network">Failed to install due to the network issue</string>
+ <!-- Toast error message for install failure because Wi-Fi isn't available although required [CHAR LIMIT=none] -->
+ <string name="installer_error_no_wifi">Failed to install because Wi-Fi isn\'t available</string>
<!-- Toast error message for install failure due to the unidentified issue [CHAR LIMIT=none] -->
<string name="installer_error_unknown">Failed to install. Try again.</string>