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