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