• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.jni_zero.CalledByNative;
21 import org.jni_zero.JNINamespace;
22 
23 import org.chromium.base.compat.ApiHelperForM;
24 import org.chromium.base.compat.ApiHelperForP;
25 
26 /** Exposes radio related information about the current device. */
27 @JNINamespace("base::android")
28 public class RadioUtils {
29     // Cached value indicating if app has ACCESS_NETWORK_STATE permission.
30     private static Boolean sHaveAccessNetworkState;
31     // Cached value indicating if app has ACCESS_WIFI_STATE permission.
32     private static Boolean sHaveAccessWifiState;
33 
RadioUtils()34     private RadioUtils() {}
35 
36     /**
37      * Return whether the current SDK supports necessary functions and the app
38      * has necessary permissions.
39      * @return True or false.
40      */
41     @CalledByNative
isSupported()42     private static boolean isSupported() {
43         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
44                 && 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(
54                                     ContextUtils.getApplicationContext(),
55                                     Manifest.permission.ACCESS_NETWORK_STATE,
56                                     Process.myPid(),
57                                     Process.myUid())
58                             == PackageManager.PERMISSION_GRANTED;
59         }
60         return sHaveAccessNetworkState;
61     }
62 
haveAccessWifiState()63     private static boolean haveAccessWifiState() {
64         // This could be racy if called on multiple threads, but races will
65         // end in the same result so it's not a problem.
66         if (sHaveAccessWifiState == null) {
67             sHaveAccessWifiState =
68                     ApiCompatibilityUtils.checkPermission(
69                                     ContextUtils.getApplicationContext(),
70                                     Manifest.permission.ACCESS_WIFI_STATE,
71                                     Process.myPid(),
72                                     Process.myUid())
73                             == PackageManager.PERMISSION_GRANTED;
74         }
75         return sHaveAccessWifiState;
76     }
77 
78     /**
79      * Return whether the device is currently connected to a wifi network.
80      * @return True or false.
81      */
82     @CalledByNative
83     @RequiresApi(Build.VERSION_CODES.P)
84     @SuppressWarnings("AssertionSideEffect") // isSupported() caches via sHaveAccessNetworkState.
isWifiConnected()85     private static boolean isWifiConnected() {
86         assert isSupported();
87         try (TraceEvent te = TraceEvent.scoped("RadioUtils::isWifiConnected")) {
88             ConnectivityManager connectivityManager =
89                     (ConnectivityManager)
90                             ContextUtils.getApplicationContext()
91                                     .getSystemService(Context.CONNECTIVITY_SERVICE);
92             Network network = ApiHelperForM.getActiveNetwork(connectivityManager);
93             if (network == null) {
94                 return false;
95             }
96             NetworkCapabilities networkCapabilities =
97                     connectivityManager.getNetworkCapabilities(network);
98             if (networkCapabilities == null) {
99                 return false;
100             }
101             return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
102         }
103     }
104 
105     /**
106      * Return current cell signal level.
107      * @return Signal level from 0 (no signal) to 4 (good signal) or -1 in case of error.
108      */
109     @CalledByNative
110     @RequiresApi(Build.VERSION_CODES.P)
111     @SuppressWarnings("AssertionSideEffect") // isSupported() caches via sHaveAccessNetworkState.
getCellSignalLevel()112     private static int getCellSignalLevel() {
113         assert isSupported();
114         try (TraceEvent te = TraceEvent.scoped("RadioUtils::getCellSignalLevel")) {
115             TelephonyManager telephonyManager =
116                     (TelephonyManager)
117                             ContextUtils.getApplicationContext()
118                                     .getSystemService(Context.TELEPHONY_SERVICE);
119             int level = -1;
120             try {
121                 SignalStrength signalStrength = ApiHelperForP.getSignalStrength(telephonyManager);
122                 if (signalStrength != null) {
123                     level = signalStrength.getLevel();
124                 }
125             } catch (java.lang.SecurityException e) {
126                 // Sometimes SignalStrength.getLevel() requires extra permissions
127                 // that Chrome doesn't have. See crbug.com/1150536.
128             }
129             return level;
130         }
131     }
132 
133     /**
134      * Return current cell data activity.
135      * @return 0 - none, 1 - in, 2 - out, 3 - in/out, 4 - dormant, or -1 in case of error.
136      */
137     @CalledByNative
138     @RequiresApi(Build.VERSION_CODES.P)
139     @SuppressWarnings("AssertionSideEffect") // isSupported() caches via sHaveAccessNetworkState.
getCellDataActivity()140     private static int getCellDataActivity() {
141         assert isSupported();
142         try (TraceEvent te = TraceEvent.scoped("RadioUtils::getCellDataActivity")) {
143             TelephonyManager telephonyManager =
144                     (TelephonyManager)
145                             ContextUtils.getApplicationContext()
146                                     .getSystemService(Context.TELEPHONY_SERVICE);
147             try {
148                 return telephonyManager.getDataActivity();
149             } catch (java.lang.SecurityException e) {
150                 // Just in case getDataActivity() requires extra permissions.
151                 return -1;
152             }
153         }
154     }
155 }
156