1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; 20 import static android.content.pm.PackageManager.FEATURE_DEVICE_ADMIN; 21 22 import android.annotation.NonNull; 23 import android.app.ActivityManager; 24 import android.app.AlertDialog; 25 import android.app.Notification; 26 import android.app.PendingIntent; 27 import android.app.admin.DevicePolicyManager; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.PackageManager; 33 import android.content.pm.ResolveInfo; 34 import android.content.res.Configuration; 35 import android.database.ContentObserver; 36 import android.net.TrafficStats; 37 import android.net.Uri; 38 import android.net.ip.IpClientCallbacks; 39 import android.net.ip.IpClientUtil; 40 import android.os.PersistableBundle; 41 import android.os.Process; 42 import android.os.UserHandle; 43 import android.os.WorkSource; 44 import android.provider.Settings; 45 import android.security.KeyChain; 46 import android.telephony.CarrierConfigManager; 47 import android.util.Log; 48 import android.widget.Toast; 49 50 import com.android.modules.utils.build.SdkLevel; 51 import com.android.wifi.resources.R; 52 53 import java.util.List; 54 import java.util.NoSuchElementException; 55 56 /** 57 * This class allows overriding objects with mocks to write unit tests 58 */ 59 public class FrameworkFacade { 60 public static final String TAG = "FrameworkFacade"; 61 62 private ContentResolver mContentResolver = null; 63 private CarrierConfigManager mCarrierConfigManager = null; 64 private ActivityManager mActivityManager = null; 65 66 // verbose logging controlled by user 67 private static final int VERBOSE_LOGGING_ALWAYS_ON_LEVEL_NONE = 0; 68 // verbose logging on by default for userdebug 69 private static final int VERBOSE_LOGGING_ALWAYS_ON_LEVEL_USERDEBUG = 1; 70 // verbose logging on by default for all builds --> 71 private static final int VERBOSE_LOGGING_ALWAYS_ON_LEVEL_ALL = 2; 72 getContentResolver(Context context)73 private ContentResolver getContentResolver(Context context) { 74 if (mContentResolver == null) { 75 mContentResolver = context.getContentResolver(); 76 } 77 return mContentResolver; 78 } 79 getCarrierConfigManager(Context context)80 private CarrierConfigManager getCarrierConfigManager(Context context) { 81 if (mCarrierConfigManager == null) { 82 mCarrierConfigManager = 83 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 84 } 85 return mCarrierConfigManager; 86 } 87 getActivityManager(Context context)88 private ActivityManager getActivityManager(Context context) { 89 if (mActivityManager == null) { 90 mActivityManager = 91 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 92 } 93 return mActivityManager; 94 } 95 96 /** 97 * Mockable getter for user context from ActivityManager 98 */ getUserContext(Context context)99 public Context getUserContext(Context context) { 100 return context.createContextAsUser(UserHandle.of(ActivityManager.getCurrentUser()), 0); 101 } 102 103 /** 104 * Mockable setter for Settings.Global 105 */ setIntegerSetting(ContentResolver contentResolver, String name, int value)106 public boolean setIntegerSetting(ContentResolver contentResolver, String name, int value) { 107 return Settings.Global.putInt(contentResolver, name, value); 108 } 109 110 /** 111 * Mockable getter for Settings.Global 112 */ getIntegerSetting(ContentResolver contentResolver, String name, int def)113 public int getIntegerSetting(ContentResolver contentResolver, String name, int def) { 114 return Settings.Global.getInt(contentResolver, name, def); 115 } 116 setIntegerSetting(Context context, String name, int def)117 public boolean setIntegerSetting(Context context, String name, int def) { 118 return Settings.Global.putInt(getContentResolver(context), name, def); 119 } 120 getIntegerSetting(Context context, String name, int def)121 public int getIntegerSetting(Context context, String name, int def) { 122 return Settings.Global.getInt(getContentResolver(context), name, def); 123 } 124 125 /** 126 * Mockable getter for Settings.Global.getInt with SettingNotFoundException 127 */ getIntegerSetting(ContentResolver contentResolver, String name)128 public int getIntegerSetting(ContentResolver contentResolver, String name) 129 throws Settings.SettingNotFoundException { 130 return Settings.Global.getInt(contentResolver, name); 131 } 132 getLongSetting(Context context, String name, long def)133 public long getLongSetting(Context context, String name, long def) { 134 return Settings.Global.getLong(getContentResolver(context), name, def); 135 } 136 setStringSetting(Context context, String name, String def)137 public boolean setStringSetting(Context context, String name, String def) { 138 return Settings.Global.putString(getContentResolver(context), name, def); 139 } 140 getStringSetting(Context context, String name)141 public String getStringSetting(Context context, String name) { 142 return Settings.Global.getString(getContentResolver(context), name); 143 } 144 145 /** 146 * Mockable facade to Settings.Secure.getInt(.). 147 */ getSecureIntegerSetting(Context context, String name, int def)148 public int getSecureIntegerSetting(Context context, String name, int def) { 149 return Settings.Secure.getInt(context.getContentResolver(), name, def); 150 } 151 152 /** 153 * Mockable facade to Settings.Secure.putInt(.). 154 */ setSecureIntegerSetting(Context context, String name, int def)155 public boolean setSecureIntegerSetting(Context context, String name, int def) { 156 return Settings.Secure.putInt(context.getContentResolver(), name, def); 157 } 158 159 /** 160 * Mockable facade to Settings.Secure.getString(.). 161 */ getSecureStringSetting(Context context, String name)162 public String getSecureStringSetting(Context context, String name) { 163 return Settings.Secure.getString(context.getContentResolver(), name); 164 } 165 166 /** 167 * Returns whether the device is in NIAP mode or not. 168 */ isNiapModeOn(Context context)169 public boolean isNiapModeOn(Context context) { 170 boolean isNiapModeEnabled = context.getResources().getBoolean( 171 R.bool.config_wifiNiapModeEnabled); 172 if (isNiapModeEnabled) return true; 173 174 DevicePolicyManager devicePolicyManager = 175 context.getSystemService(DevicePolicyManager.class); 176 if (devicePolicyManager == null 177 && context.getPackageManager().hasSystemFeature(FEATURE_DEVICE_ADMIN)) { 178 Log.e(TAG, "Error retrieving DPM service"); 179 } 180 if (devicePolicyManager == null) return false; 181 return devicePolicyManager.isCommonCriteriaModeEnabled(null); 182 } 183 184 /** 185 * Helper method for classes to register a ContentObserver 186 * {@see ContentResolver#registerContentObserver(Uri,boolean,ContentObserver)}. 187 * 188 * @param context 189 * @param uri 190 * @param notifyForDescendants 191 * @param contentObserver 192 */ registerContentObserver(Context context, Uri uri, boolean notifyForDescendants, ContentObserver contentObserver)193 public void registerContentObserver(Context context, Uri uri, 194 boolean notifyForDescendants, ContentObserver contentObserver) { 195 getContentResolver(context).registerContentObserver(uri, notifyForDescendants, 196 contentObserver); 197 } 198 199 /** 200 * Helper method for classes to unregister a ContentObserver 201 * {@see ContentResolver#unregisterContentObserver(ContentObserver)}. 202 * 203 * @param context 204 * @param contentObserver 205 */ unregisterContentObserver(Context context, ContentObserver contentObserver)206 public void unregisterContentObserver(Context context, ContentObserver contentObserver) { 207 getContentResolver(context).unregisterContentObserver(contentObserver); 208 } 209 getBroadcast(Context context, int requestCode, Intent intent, int flags)210 public PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags) { 211 return PendingIntent.getBroadcast(context, requestCode, intent, flags); 212 } 213 214 /** 215 * Wrapper for {@link PendingIntent#getActivity} using the current foreground user. 216 */ getActivity(Context context, int requestCode, Intent intent, int flags)217 public PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags) { 218 return PendingIntent.getActivity(context.createContextAsUser(UserHandle.CURRENT, 0), 219 requestCode, intent, flags); 220 } 221 getConfigWiFiDisableInECBM(Context context)222 public boolean getConfigWiFiDisableInECBM(Context context) { 223 CarrierConfigManager configManager = getCarrierConfigManager(context); 224 if (configManager == null) { 225 return false; 226 } 227 PersistableBundle bundle = configManager.getConfig(); 228 if (bundle == null) { 229 return false; 230 } 231 return bundle.getBoolean(CarrierConfigManager.KEY_CONFIG_WIFI_DISABLE_IN_ECBM); 232 } 233 getTxPackets(String iface)234 public long getTxPackets(String iface) { 235 return TrafficStats.getTxPackets(iface); 236 } 237 getRxPackets(String iface)238 public long getRxPackets(String iface) { 239 return TrafficStats.getRxPackets(iface); 240 } 241 getTxBytes(String iface)242 public long getTxBytes(String iface) { 243 return SdkLevel.isAtLeastS() ? TrafficStats.getTxBytes(iface) : 0; 244 } 245 getRxBytes(String iface)246 public long getRxBytes(String iface) { 247 return SdkLevel.isAtLeastS() ? TrafficStats.getRxBytes(iface) : 0; 248 } 249 250 /** 251 * Request a new IpClient to be created asynchronously. 252 * @param context Context to use for creation. 253 * @param iface Interface the client should act on. 254 * @param callback IpClient event callbacks. 255 */ makeIpClient(Context context, String iface, IpClientCallbacks callback)256 public void makeIpClient(Context context, String iface, IpClientCallbacks callback) { 257 IpClientUtil.makeIpClient(context, iface, callback); 258 } 259 260 /** 261 * Check if the provided uid is the app in the foreground. 262 * @param uid the uid to check 263 * @return true if the app is in the foreground, false otherwise 264 */ isAppForeground(Context context, int uid)265 public boolean isAppForeground(Context context, int uid) { 266 ActivityManager activityManager = getActivityManager(context); 267 if (activityManager == null) return false; 268 return activityManager.getUidImportance(uid) <= IMPORTANCE_VISIBLE; 269 } 270 271 /** 272 * Create a new instance of {@link Notification.Builder}. 273 * @param context reference to a Context 274 * @param channelId ID of the notification channel 275 * @return an instance of Notification.Builder 276 */ makeNotificationBuilder(Context context, String channelId)277 public Notification.Builder makeNotificationBuilder(Context context, String channelId) { 278 return new Notification.Builder(context, channelId); 279 } 280 281 /** 282 * Starts supplicant 283 */ startSupplicant()284 public boolean startSupplicant() { 285 try { 286 SupplicantManager.start(); 287 return true; 288 } catch (NoSuchElementException e) { 289 return false; 290 } 291 } 292 293 /** 294 * Stops supplicant 295 */ stopSupplicant()296 public void stopSupplicant() { 297 SupplicantManager.stop(); 298 } 299 300 /** 301 * Create a new instance of {@link AlertDialog.Builder}. 302 * @param context reference to a Context 303 * @return an instance of AlertDialog.Builder 304 * @deprecated Use {@link WifiDialogManager#createSimpleDialog} instead, or create another 305 * dialog type in WifiDialogManager. 306 */ makeAlertDialogBuilder(Context context)307 public AlertDialog.Builder makeAlertDialogBuilder(Context context) { 308 boolean isDarkTheme = (context.getResources().getConfiguration().uiMode 309 & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; 310 return new AlertDialog.Builder(context, isDarkTheme 311 ? android.R.style.Theme_DeviceDefault_Dialog_Alert : 0); 312 } 313 314 /** 315 * Show a toast message 316 * @param context reference to a Context 317 * @param text the message to display 318 */ showToast(Context context, String text)319 public void showToast(Context context, String text) { 320 Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); 321 toast.show(); 322 } 323 324 /** 325 * Wrapper for {@link TrafficStats#getMobileTxBytes}. 326 */ getMobileTxBytes()327 public long getMobileTxBytes() { 328 return TrafficStats.getMobileTxBytes(); 329 } 330 331 /** 332 * Wrapper for {@link TrafficStats#getMobileRxBytes}. 333 */ getMobileRxBytes()334 public long getMobileRxBytes() { 335 return TrafficStats.getMobileRxBytes(); 336 } 337 338 /** 339 * Wrapper for {@link TrafficStats#getTotalTxBytes}. 340 */ getTotalTxBytes()341 public long getTotalTxBytes() { 342 return TrafficStats.getTotalTxBytes(); 343 } 344 345 /** 346 * Wrapper for {@link TrafficStats#getTotalRxBytes}. 347 */ getTotalRxBytes()348 public long getTotalRxBytes() { 349 return TrafficStats.getTotalRxBytes(); 350 } 351 352 private String mSettingsPackageName; 353 354 /** 355 * @return Get settings package name. 356 */ getSettingsPackageName(@onNull Context context)357 public String getSettingsPackageName(@NonNull Context context) { 358 if (mSettingsPackageName != null) return mSettingsPackageName; 359 360 Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS); 361 List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivitiesAsUser( 362 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DEFAULT_ONLY, 363 UserHandle.of(ActivityManager.getCurrentUser())); 364 if (resolveInfos == null || resolveInfos.isEmpty()) { 365 Log.e(TAG, "Failed to resolve wifi settings activity"); 366 return null; 367 } 368 // Pick the first one if there are more than 1 since the list is ordered from best to worst. 369 mSettingsPackageName = resolveInfos.get(0).activityInfo.packageName; 370 return mSettingsPackageName; 371 } 372 373 /** 374 * @return Get a worksource to blame settings apps. 375 */ getSettingsWorkSource(Context context)376 public WorkSource getSettingsWorkSource(Context context) { 377 String settingsPackageName = getSettingsPackageName(context); 378 if (settingsPackageName == null) return new WorkSource(Process.SYSTEM_UID); 379 return new WorkSource(Process.SYSTEM_UID, settingsPackageName); 380 } 381 382 /** 383 * Returns whether a KeyChain key is granted to the WiFi stack. 384 */ hasWifiKeyGrantAsUser(Context context, UserHandle user, String alias)385 public boolean hasWifiKeyGrantAsUser(Context context, UserHandle user, String alias) { 386 return KeyChain.hasWifiKeyGrantAsUser(context, user, alias); 387 } 388 389 /** 390 * Returns grant string for a given KeyChain alias or null if key not granted. 391 */ getWifiKeyGrantAsUser(Context context, UserHandle user, String alias)392 public String getWifiKeyGrantAsUser(Context context, UserHandle user, String alias) { 393 if (!SdkLevel.isAtLeastS()) { 394 return null; 395 } 396 return KeyChain.getWifiKeyGrantAsUser(context, user, alias); 397 } 398 399 /** 400 * Check if the request comes from foreground app/service. 401 * @param context Application context 402 * @param requestorPackageName requestor package name 403 * @return true if the requestor is foreground app/service. 404 */ isRequestFromForegroundAppOrService(Context context, @NonNull String requestorPackageName)405 public boolean isRequestFromForegroundAppOrService(Context context, 406 @NonNull String requestorPackageName) { 407 ActivityManager activityManager = getActivityManager(context); 408 if (activityManager == null) return false; 409 try { 410 return activityManager.getPackageImportance(requestorPackageName) 411 <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 412 } catch (SecurityException e) { 413 Log.e(TAG, "Failed to check the app state", e); 414 return false; 415 } 416 } 417 418 /** 419 * Check if the request comes from foreground app. 420 * @param context Application context 421 * @param requestorPackageName requestor package name 422 * @return true if requestor is foreground app. 423 */ isRequestFromForegroundApp(Context context, @NonNull String requestorPackageName)424 public boolean isRequestFromForegroundApp(Context context, 425 @NonNull String requestorPackageName) { 426 ActivityManager activityManager = getActivityManager(context); 427 if (activityManager == null) return false; 428 try { 429 return activityManager.getPackageImportance(requestorPackageName) 430 <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 431 } catch (SecurityException e) { 432 Log.e(TAG, "Failed to check the app state", e); 433 return false; 434 } 435 } 436 437 /** 438 * Check if the verbose always on is enabled 439 * @param alwaysOnLevel verbose logging always on level 440 * @param buildProperties build property of current build 441 * @return true if verbose always on is enabled on current build 442 */ isVerboseLoggingAlwaysOn(int alwaysOnLevel, @NonNull BuildProperties buildProperties)443 public boolean isVerboseLoggingAlwaysOn(int alwaysOnLevel, 444 @NonNull BuildProperties buildProperties) { 445 switch (alwaysOnLevel) { 446 // If the overlay setting enabled for all builds 447 case VERBOSE_LOGGING_ALWAYS_ON_LEVEL_ALL: 448 return true; 449 //If the overlay setting enabled for userdebug builds only 450 case VERBOSE_LOGGING_ALWAYS_ON_LEVEL_USERDEBUG: 451 // If it is a userdebug build 452 if (buildProperties.isUserdebugBuild()) return true; 453 break; 454 case VERBOSE_LOGGING_ALWAYS_ON_LEVEL_NONE: 455 // nothing 456 break; 457 default: 458 Log.e(TAG, "Unrecognized config_wifiVerboseLoggingAlwaysOnLevel " + alwaysOnLevel); 459 break; 460 } 461 return false; 462 } 463 464 /** 465 * Return the (displayable) application name corresponding to the (uid, packageName). 466 */ getAppName(Context context, @NonNull String packageName, int uid)467 public @NonNull CharSequence getAppName(Context context, @NonNull String packageName, int uid) { 468 ApplicationInfo applicationInfo = null; 469 try { 470 applicationInfo = context.getPackageManager().getApplicationInfoAsUser( 471 packageName, 0, UserHandle.getUserHandleForUid(uid)); 472 } catch (PackageManager.NameNotFoundException e) { 473 Log.e(TAG, "Failed to find app name for " + packageName); 474 return ""; 475 } 476 CharSequence appName = context.getPackageManager().getApplicationLabel(applicationInfo); 477 return (appName != null) ? appName : ""; 478 } 479 } 480