blob: fdf3019140c216b8cb1ed63df94becfdf3e87519 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Build;
import android.os.Process;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import androidx.annotation.RequiresApi;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.chromium.base.compat.ApiHelperForM;
import org.chromium.base.compat.ApiHelperForP;
/** Exposes radio related information about the current device. */
@JNINamespace("base::android")
public class RadioUtils {
// Cached value indicating if app has ACCESS_NETWORK_STATE permission.
private static Boolean sHaveAccessNetworkState;
// Cached value indicating if app has ACCESS_WIFI_STATE permission.
private static Boolean sHaveAccessWifiState;
private RadioUtils() {}
/**
* Return whether the current SDK supports necessary functions and the app
* has necessary permissions.
* @return True or false.
*/
@CalledByNative
private static boolean isSupported() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& haveAccessNetworkState()
&& haveAccessWifiState();
}
private static boolean haveAccessNetworkState() {
// This could be racy if called on multiple threads, but races will
// end in the same result so it's not a problem.
if (sHaveAccessNetworkState == null) {
sHaveAccessNetworkState =
ApiCompatibilityUtils.checkPermission(
ContextUtils.getApplicationContext(),
Manifest.permission.ACCESS_NETWORK_STATE,
Process.myPid(),
Process.myUid())
== PackageManager.PERMISSION_GRANTED;
}
return sHaveAccessNetworkState;
}
private static boolean haveAccessWifiState() {
// This could be racy if called on multiple threads, but races will
// end in the same result so it's not a problem.
if (sHaveAccessWifiState == null) {
sHaveAccessWifiState =
ApiCompatibilityUtils.checkPermission(
ContextUtils.getApplicationContext(),
Manifest.permission.ACCESS_WIFI_STATE,
Process.myPid(),
Process.myUid())
== PackageManager.PERMISSION_GRANTED;
}
return sHaveAccessWifiState;
}
/**
* Return whether the device is currently connected to a wifi network.
* @return True or false.
*/
@CalledByNative
@RequiresApi(Build.VERSION_CODES.P)
@SuppressWarnings("AssertionSideEffect") // isSupported() caches via sHaveAccessNetworkState.
private static boolean isWifiConnected() {
assert isSupported();
try (TraceEvent te = TraceEvent.scoped("RadioUtils::isWifiConnected")) {
ConnectivityManager connectivityManager =
(ConnectivityManager)
ContextUtils.getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
Network network = ApiHelperForM.getActiveNetwork(connectivityManager);
if (network == null) {
return false;
}
NetworkCapabilities networkCapabilities =
connectivityManager.getNetworkCapabilities(network);
if (networkCapabilities == null) {
return false;
}
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
}
}
/**
* Return current cell signal level.
* @return Signal level from 0 (no signal) to 4 (good signal) or -1 in case of error.
*/
@CalledByNative
@RequiresApi(Build.VERSION_CODES.P)
@SuppressWarnings("AssertionSideEffect") // isSupported() caches via sHaveAccessNetworkState.
private static int getCellSignalLevel() {
assert isSupported();
try (TraceEvent te = TraceEvent.scoped("RadioUtils::getCellSignalLevel")) {
TelephonyManager telephonyManager =
(TelephonyManager)
ContextUtils.getApplicationContext()
.getSystemService(Context.TELEPHONY_SERVICE);
int level = -1;
try {
SignalStrength signalStrength = ApiHelperForP.getSignalStrength(telephonyManager);
if (signalStrength != null) {
level = signalStrength.getLevel();
}
} catch (java.lang.SecurityException e) {
// Sometimes SignalStrength.getLevel() requires extra permissions
// that Chrome doesn't have. See crbug.com/1150536.
}
return level;
}
}
/**
* Return current cell data activity.
* @return 0 - none, 1 - in, 2 - out, 3 - in/out, 4 - dormant, or -1 in case of error.
*/
@CalledByNative
@RequiresApi(Build.VERSION_CODES.P)
@SuppressWarnings("AssertionSideEffect") // isSupported() caches via sHaveAccessNetworkState.
private static int getCellDataActivity() {
assert isSupported();
try (TraceEvent te = TraceEvent.scoped("RadioUtils::getCellDataActivity")) {
TelephonyManager telephonyManager =
(TelephonyManager)
ContextUtils.getApplicationContext()
.getSystemService(Context.TELEPHONY_SERVICE);
try {
return telephonyManager.getDataActivity();
} catch (java.lang.SecurityException e) {
// Just in case getDataActivity() requires extra permissions.
return -1;
}
}
}
}