1 // Copyright 2020 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base; 6 7 import android.Manifest; 8 import android.content.Context; 9 import android.content.pm.PackageManager; 10 import android.net.ConnectivityManager; 11 import android.net.Network; 12 import android.net.NetworkCapabilities; 13 import android.os.Build; 14 import android.os.Process; 15 import android.telephony.SignalStrength; 16 import android.telephony.TelephonyManager; 17 18 import androidx.annotation.RequiresApi; 19 20 import org.chromium.base.annotations.CalledByNative; 21 import org.chromium.base.annotations.JNINamespace; 22 import org.chromium.base.compat.ApiHelperForM; 23 import org.chromium.base.compat.ApiHelperForP; 24 25 /** 26 * Exposes radio related information about the current device. 27 */ 28 @JNINamespace("base::android") 29 public class RadioUtils { 30 // Cached value indicating if app has ACCESS_NETWORK_STATE permission. 31 private static Boolean sHaveAccessNetworkState; 32 // Cached value indicating if app has ACCESS_WIFI_STATE permission. 33 private static Boolean sHaveAccessWifiState; 34 RadioUtils()35 private RadioUtils() {} 36 37 /** 38 * Return whether the current SDK supports necessary functions and the app 39 * has necessary permissions. 40 * @return True or false. 41 */ 42 @CalledByNative isSupported()43 private static boolean isSupported() { 44 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && haveAccessNetworkState() 45 && haveAccessWifiState(); 46 } 47 haveAccessNetworkState()48 private static boolean haveAccessNetworkState() { 49 // This could be racy if called on multiple threads, but races will 50 // end in the same result so it's not a problem. 51 if (sHaveAccessNetworkState == null) { 52 sHaveAccessNetworkState = 53 ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationContext(), 54 Manifest.permission.ACCESS_NETWORK_STATE, Process.myPid(), 55 Process.myUid()) 56 == PackageManager.PERMISSION_GRANTED; 57 } 58 return sHaveAccessNetworkState; 59 } 60 haveAccessWifiState()61 private static boolean haveAccessWifiState() { 62 // This could be racy if called on multiple threads, but races will 63 // end in the same result so it's not a problem. 64 if (sHaveAccessWifiState == null) { 65 sHaveAccessWifiState = 66 ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationContext(), 67 Manifest.permission.ACCESS_WIFI_STATE, Process.myPid(), Process.myUid()) 68 == PackageManager.PERMISSION_GRANTED; 69 } 70 return sHaveAccessWifiState; 71 } 72 73 /** 74 * Return whether the device is currently connected to a wifi network. 75 * @return True or false. 76 */ 77 @CalledByNative 78 @RequiresApi(Build.VERSION_CODES.P) isWifiConnected()79 private static boolean isWifiConnected() { 80 assert isSupported(); 81 try (TraceEvent te = TraceEvent.scoped("RadioUtils::isWifiConnected")) { 82 ConnectivityManager connectivityManager = 83 (ConnectivityManager) ContextUtils.getApplicationContext().getSystemService( 84 Context.CONNECTIVITY_SERVICE); 85 Network network = ApiHelperForM.getActiveNetwork(connectivityManager); 86 if (network == null) { 87 return false; 88 } 89 NetworkCapabilities networkCapabilities = 90 connectivityManager.getNetworkCapabilities(network); 91 if (networkCapabilities == null) { 92 return false; 93 } 94 return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); 95 } 96 } 97 98 /** 99 * Return current cell signal level. 100 * @return Signal level from 0 (no signal) to 4 (good signal) or -1 in case of error. 101 */ 102 @CalledByNative 103 @RequiresApi(Build.VERSION_CODES.P) getCellSignalLevel()104 private static int getCellSignalLevel() { 105 assert isSupported(); 106 try (TraceEvent te = TraceEvent.scoped("RadioUtils::getCellSignalLevel")) { 107 TelephonyManager telephonyManager = 108 (TelephonyManager) ContextUtils.getApplicationContext().getSystemService( 109 Context.TELEPHONY_SERVICE); 110 int level = -1; 111 try { 112 SignalStrength signalStrength = ApiHelperForP.getSignalStrength(telephonyManager); 113 if (signalStrength != null) { 114 level = signalStrength.getLevel(); 115 } 116 } catch (java.lang.SecurityException e) { 117 // Sometimes SignalStrength.getLevel() requires extra permissions 118 // that Chrome doesn't have. See crbug.com/1150536. 119 } 120 return level; 121 } 122 } 123 124 /** 125 * Return current cell data activity. 126 * @return 0 - none, 1 - in, 2 - out, 3 - in/out, 4 - dormant, or -1 in case of error. 127 */ 128 @CalledByNative 129 @RequiresApi(Build.VERSION_CODES.P) getCellDataActivity()130 private static int getCellDataActivity() { 131 assert isSupported(); 132 try (TraceEvent te = TraceEvent.scoped("RadioUtils::getCellDataActivity")) { 133 TelephonyManager telephonyManager = 134 (TelephonyManager) ContextUtils.getApplicationContext().getSystemService( 135 Context.TELEPHONY_SERVICE); 136 try { 137 return telephonyManager.getDataActivity(); 138 } catch (java.lang.SecurityException e) { 139 // Just in case getDataActivity() requires extra permissions. 140 return -1; 141 } 142 } 143 } 144 } 145