• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.util;
18 
19 import android.Manifest;
20 import android.annotation.Nullable;
21 import android.app.AppOpsManager;
22 import android.app.admin.DevicePolicyManager;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageManager;
27 import android.location.LocationManager;
28 import android.net.NetworkStack;
29 import android.os.Binder;
30 import android.os.Build;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.provider.Settings;
34 import android.util.EventLog;
35 import android.util.Log;
36 import android.util.Pair;
37 
38 import com.android.internal.annotations.GuardedBy;
39 import com.android.modules.utils.build.SdkLevel;
40 import com.android.server.wifi.FrameworkFacade;
41 import com.android.server.wifi.WifiInjector;
42 import com.android.server.wifi.WifiLog;
43 
44 import java.util.Arrays;
45 
46 /**
47  * A wifi permissions utility assessing permissions
48  * for getting scan results by a package.
49  */
50 public class WifiPermissionsUtil {
51     private static final String TAG = "WifiPermissionsUtil";
52 
53     private static final int APP_INFO_FLAGS_SYSTEM_APP =
54             ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
55     private final WifiPermissionsWrapper mWifiPermissionsWrapper;
56     private final Context mContext;
57     private final FrameworkFacade mFrameworkFacade;
58     private final AppOpsManager mAppOps;
59     private final UserManager mUserManager;
60     private final Object mLock = new Object();
61     @GuardedBy("mLock")
62     private LocationManager mLocationManager;
63     private WifiLog mLog;
64     private boolean mVerboseLoggingEnabled;
65 
WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper, Context context, UserManager userManager, WifiInjector wifiInjector)66     public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper,
67             Context context, UserManager userManager, WifiInjector wifiInjector) {
68         mWifiPermissionsWrapper = wifiPermissionsWrapper;
69         mContext = context;
70         mFrameworkFacade = wifiInjector.getFrameworkFacade();
71         mUserManager = userManager;
72         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
73         mLog = wifiInjector.makeLog(TAG);
74     }
75 
76     /**
77      * Checks if the app has the permission to override Wi-Fi network configuration or not.
78      *
79      * @param uid uid of the app.
80      * @return true if the app does have the permission, false otherwise.
81      */
checkConfigOverridePermission(int uid)82     public boolean checkConfigOverridePermission(int uid) {
83         int permission = mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid);
84         return permission == PackageManager.PERMISSION_GRANTED;
85     }
86 
87     /**
88      * Check and enforce Coarse or Fine Location permission (depending on target SDK).
89      *
90      * @param pkgName PackageName of the application requesting access
91      * @param featureId The feature in the package
92      * @param uid The uid of the package
93      */
enforceLocationPermission(String pkgName, @Nullable String featureId, int uid)94     public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid) {
95         if (!checkCallersLocationPermission(pkgName, featureId,
96                 uid, /* coarseForTargetSdkLessThanQ */ true, null)) {
97             throw new SecurityException(
98                     "UID " + uid + " does not have Coarse/Fine Location permission");
99         }
100     }
101 
102     /**
103      * Checks whether than the target SDK of the package is less than the specified version code.
104      */
isTargetSdkLessThan(String packageName, int versionCode, int callingUid)105     public boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
106         long ident = Binder.clearCallingIdentity();
107         try {
108             final int targetSdkVersion;
109             if (SdkLevel.isAtLeastS()) {
110                 // >= S, use the lightweight API to just get the target SDK version.
111                 Context userContext = createPackageContextAsUser(callingUid);
112                 if (userContext == null) return false;
113                 targetSdkVersion = userContext.getPackageManager().getTargetSdkVersion(packageName);
114             } else {
115                 // < S, use the heavyweight API to get all package info.
116                 targetSdkVersion = mContext.getPackageManager().getApplicationInfoAsUser(
117                         packageName, 0,
118                         UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion;
119             }
120             return targetSdkVersion < versionCode;
121         } catch (PackageManager.NameNotFoundException e) {
122             // In case of exception, assume unknown app (more strict checking)
123             // Note: This case will never happen since checkPackage is
124             // called to verify validity before checking App's version.
125             return false;
126         } finally {
127             Binder.restoreCallingIdentity(ident);
128         }
129     }
130 
131     /**
132      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
133      * android.Manifest.permission.ACCESS_FINE_LOCATION (depending on config/targetSDK leve)
134      * and a corresponding app op is allowed for this package and uid.
135      *
136      * @param pkgName PackageName of the application requesting access
137      * @param featureId The feature in the package
138      * @param uid The uid of the package
139      * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
140      *                                    else (false or targetSDK >= Q) then will check for FINE
141      * @param message A message describing why the permission was checked. Only needed if this is
142      *                not inside of a two-way binder call from the data receiver
143      */
checkCallersLocationPermission(String pkgName, @Nullable String featureId, int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message)144     public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
145             int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
146         boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
147 
148         String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
149         if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
150             // Having FINE permission implies having COARSE permission (but not the reverse)
151             permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
152         }
153         if (mWifiPermissionsWrapper.getUidPermission(permissionType, uid)
154                 == PackageManager.PERMISSION_DENIED) {
155             if (mVerboseLoggingEnabled) {
156                 Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): uid " + uid
157                         + " doesn't have permission " + permissionType);
158             }
159             return false;
160         }
161 
162         // Always checking FINE - even if will not enforce. This will record the request for FINE
163         // so that a location request by the app is surfaced to the user.
164         boolean isFineLocationAllowed = noteAppOpAllowed(
165                 AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
166         if (isFineLocationAllowed) {
167             if (mVerboseLoggingEnabled) {
168                 Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): ok because uid " + uid
169                         + " has app-op " + AppOpsManager.OPSTR_FINE_LOCATION);
170             }
171             return true;
172         }
173         if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
174             boolean allowed = noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName,
175                     featureId, uid, message);
176             if (mVerboseLoggingEnabled) {
177                 Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): returning " + allowed
178                         + " because uid " + uid + (allowed ? "has" : "doesn't have") + " app-op "
179                         + AppOpsManager.OPSTR_COARSE_LOCATION);
180             }
181             return allowed;
182         }
183         if (mVerboseLoggingEnabled) {
184             Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): returning false for " + uid
185                     + ": coarseForTargetSdkLessThanQ=" + coarseForTargetSdkLessThanQ
186                     + ", isTargetSdkLessThanQ=" + isTargetSdkLessThanQ);
187 
188         }
189         return false;
190     }
191 
192     /**
193      * Check and enforce Fine Location permission.
194      *
195      * @param pkgName PackageName of the application requesting access
196      * @param featureId The feature in the package
197      * @param uid The uid of the package
198      */
enforceFineLocationPermission(String pkgName, @Nullable String featureId, int uid)199     public void enforceFineLocationPermission(String pkgName, @Nullable String featureId,
200             int uid) {
201         if (!checkCallersFineLocationPermission(pkgName, featureId, uid, false)) {
202             throw new SecurityException("UID " + uid + " does not have Fine Location permission");
203         }
204     }
205 
206     /**
207      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION
208      * and a corresponding app op is allowed for this package and uid.
209      *
210      * @param pkgName PackageName of the application requesting access
211      * @param featureId The feature in the package
212      * @param uid The uid of the package
213      * @param hideFromAppOps True to invoke {@link AppOpsManager#checkOp(int, int, String)}, false
214      *                       to invoke {@link AppOpsManager#noteOp(String, int, String, String,
215      *                       String)}.
216      */
checkCallersFineLocationPermission(String pkgName, @Nullable String featureId, int uid, boolean hideFromAppOps)217     private boolean checkCallersFineLocationPermission(String pkgName, @Nullable String featureId,
218             int uid, boolean hideFromAppOps) {
219         // Having FINE permission implies having COARSE permission (but not the reverse)
220         if (mWifiPermissionsWrapper.getUidPermission(
221                 Manifest.permission.ACCESS_FINE_LOCATION, uid)
222                 == PackageManager.PERMISSION_DENIED) {
223             return false;
224         }
225         if (hideFromAppOps) {
226             // Don't note the operation, just check if the app is allowed to perform the operation.
227             return checkAppOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION, pkgName, uid);
228         } else {
229             return noteAppOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid,
230                     null);
231         }
232     }
233 
234     /**
235      * Checks that calling process has android.Manifest.permission.LOCATION_HARDWARE.
236      *
237      * @param uid The uid of the package
238      */
checkCallersHardwareLocationPermission(int uid)239     public boolean checkCallersHardwareLocationPermission(int uid) {
240         return mWifiPermissionsWrapper.getUidPermission(Manifest.permission.LOCATION_HARDWARE, uid)
241                 == PackageManager.PERMISSION_GRANTED;
242     }
243 
244     /**
245      * API to determine if the caller has permissions to get scan results. Throws SecurityException
246      * if the caller has no permission.
247      * @param pkgName package name of the application requesting access
248      * @param featureId The feature in the package
249      * @param uid The uid of the package
250      * @param message A message describing why the permission was checked. Only needed if this is
251      *                not inside of a two-way binder call from the data receiver
252      */
enforceCanAccessScanResults(String pkgName, @Nullable String featureId, int uid, @Nullable String message)253     public void enforceCanAccessScanResults(String pkgName, @Nullable String featureId, int uid,
254             @Nullable String message)
255             throws SecurityException {
256         checkPackage(uid, pkgName);
257 
258         // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_MANAGED_PROVISIONING,
259         // NETWORK_STACK & MAINLINE_NETWORK_STACK, RADIO_SCAN_WITHOUT_LOCATION are granted a bypass.
260         if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
261                 || checkNetworkManagedProvisioningPermission(uid)
262                 || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)
263                 || checkScanWithoutLocationPermission(uid)) {
264             return;
265         }
266 
267         // Location mode must be enabled
268         if (!isLocationModeEnabled()) {
269             if (mVerboseLoggingEnabled) {
270                 Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
271                         + "location is disabled");
272             }
273             // Location mode is disabled, scan results cannot be returned
274             throw new SecurityException("Location mode is disabled for the device");
275         }
276 
277         // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS permission.
278         boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid);
279         // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
280         // location information.
281         boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId,
282                 uid, /* coarseForTargetSdkLessThanQ */ true, message);
283 
284         // If neither caller or app has location access, there is no need to check
285         // any other permissions. Deny access to scan results.
286         if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
287             if (mVerboseLoggingEnabled) {
288                 Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
289                         + "canCallingUidAccessLocation=" + canCallingUidAccessLocation
290                         + ", canAppPackageUseLocation=" + canAppPackageUseLocation);
291             }
292             throw new SecurityException("UID " + uid + " has no location permission");
293         }
294         // Check if Wifi Scan request is an operation allowed for this App.
295         if (!isScanAllowedbyApps(pkgName, featureId, uid)) {
296             if (mVerboseLoggingEnabled) {
297                 Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
298                         + "doesn't have app-op " + AppOpsManager.OPSTR_WIFI_SCAN);
299             }
300             throw new SecurityException("UID " + uid + " has no wifi scan permission");
301         }
302         // If the User or profile is current, permission is granted
303         // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
304         boolean isCurrentProfile = isCurrentProfile(uid);
305         if (!isCurrentProfile && !checkInteractAcrossUsersFull(uid)) {
306             if (mVerboseLoggingEnabled) {
307                 Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
308                         + "isCurrentProfile=" + isCurrentProfile
309                         + ", checkInteractAcrossUsersFull=" + checkInteractAcrossUsersFull(uid));
310             }
311             throw new SecurityException("UID " + uid + " profile not permitted");
312         }
313     }
314 
315     /**
316      * API to determine if the caller has permissions to get scan results. Throws SecurityException
317      * if the caller has no permission.
318      * @param pkgName package name of the application requesting access
319      * @param featureId The feature in the package
320      * @param uid The uid of the package
321      * @param ignoreLocationSettings Whether this request can bypass location settings.
322      * @param hideFromAppOps Whether to note the request in app-ops logging or not.
323      *
324      * Note: This is to be used for checking permissions in the internal WifiScanner API surface
325      * for requests coming from system apps.
326      */
enforceCanAccessScanResultsForWifiScanner(String pkgName, @Nullable String featureId, int uid, boolean ignoreLocationSettings, boolean hideFromAppOps)327     public void enforceCanAccessScanResultsForWifiScanner(String pkgName,
328             @Nullable String featureId, int uid, boolean ignoreLocationSettings,
329             boolean hideFromAppOps) throws SecurityException {
330         checkPackage(uid, pkgName);
331 
332         // Location mode must be enabled
333         if (!isLocationModeEnabled()) {
334             if (ignoreLocationSettings) {
335                 mLog.w("Request from " + pkgName + " violated location settings");
336             } else {
337                 // Location mode is disabled, scan results cannot be returned
338                 throw new SecurityException("Location mode is disabled for the device");
339             }
340         }
341         // LocationAccess by App: caller must have fine & hardware Location permission to have
342         // access to location information.
343         if (!checkCallersFineLocationPermission(pkgName, featureId, uid, hideFromAppOps)
344                 || !checkCallersHardwareLocationPermission(uid)) {
345             throw new SecurityException("UID " + uid + " has no location permission");
346         }
347         // Check if Wifi Scan request is an operation allowed for this App.
348         if (!isScanAllowedbyApps(pkgName, featureId, uid)) {
349             throw new SecurityException("UID " + uid + " has no wifi scan permission");
350         }
351     }
352 
353     /**
354      *
355      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION
356      * and a corresponding app op is allowed for this package and uid
357      *
358      * @param pkgName package name of the application requesting access
359      * @param featureId The feature in the package
360      * @param uid The uid of the package
361      * @param needLocationModeEnabled indicates location mode must be enabled.
362      *
363      * @return true if caller has permission, false otherwise
364      */
checkCanAccessWifiDirect(String pkgName, @Nullable String featureId, int uid, boolean needLocationModeEnabled)365     public boolean checkCanAccessWifiDirect(String pkgName, @Nullable String featureId, int uid,
366                                             boolean needLocationModeEnabled) {
367         try {
368             checkPackage(uid, pkgName);
369         } catch (SecurityException se) {
370             Log.e(TAG, "Package check exception - " + se);
371             return false;
372         }
373 
374         // Apps with NETWORK_SETTINGS are granted a bypass.
375         if (checkNetworkSettingsPermission(uid)) {
376             return true;
377         }
378 
379         // Location mode must be enabled if needed.
380         if (needLocationModeEnabled && !isLocationModeEnabled()) {
381             Log.e(TAG, "Location mode is disabled for the device");
382             return false;
383         }
384 
385         // LocationAccess by App: caller must have Fine Location permission to have access to
386         // location information.
387         if (!checkCallersLocationPermission(pkgName, featureId, uid,
388                 /* coarseForTargetSdkLessThanQ */ false, null)) {
389             Log.e(TAG, "UID " + uid + " has no location permission");
390             return false;
391         }
392         return true;
393     }
394 
395     /**
396      * API to validate if a package name belongs to a UID. Throws SecurityException
397      * if pkgName does not belongs to a UID
398      *
399      * @param pkgName package name of the application requesting access
400      * @param uid The uid of the package
401      *
402      */
checkPackage(int uid, String pkgName)403     public void checkPackage(int uid, String pkgName) throws SecurityException {
404         if (pkgName == null) {
405             throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
406         }
407         mAppOps.checkPackage(uid, pkgName);
408     }
409 
410     /**
411      * Returns true if the caller holds PEERS_MAC_ADDRESS permission.
412      */
checkCallerHasPeersMacAddressPermission(int uid)413     private boolean checkCallerHasPeersMacAddressPermission(int uid) {
414         return mWifiPermissionsWrapper.getUidPermission(
415                 android.Manifest.permission.PEERS_MAC_ADDRESS, uid)
416                 == PackageManager.PERMISSION_GRANTED;
417     }
418 
419     /**
420      * Returns true if Wifi scan operation is allowed for this caller
421      * and package.
422      */
isScanAllowedbyApps(String pkgName, @Nullable String featureId, int uid)423     private boolean isScanAllowedbyApps(String pkgName, @Nullable String featureId, int uid) {
424         return noteAppOpAllowed(AppOpsManager.OPSTR_WIFI_SCAN, pkgName, featureId, uid, null);
425     }
426 
427     /**
428      * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
429      */
checkInteractAcrossUsersFull(int uid)430     private boolean checkInteractAcrossUsersFull(int uid) {
431         return mWifiPermissionsWrapper.getUidPermission(
432                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
433                 == PackageManager.PERMISSION_GRANTED;
434     }
435 
436     /**
437      * Returns true if the calling user is the current one or a profile of the
438      * current user.
439      */
isCurrentProfile(int uid)440     private boolean isCurrentProfile(int uid) {
441         UserHandle currentUser = UserHandle.of(mWifiPermissionsWrapper.getCurrentUser());
442         UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
443         return currentUser.equals(callingUser)
444                 || mUserManager.isSameProfileGroup(currentUser, callingUser);
445     }
446 
noteAppOpAllowed(String op, String pkgName, @Nullable String featureId, int uid, @Nullable String message)447     private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
448             int uid, @Nullable String message) {
449         return mAppOps.noteOp(op, uid, pkgName, featureId, message) == AppOpsManager.MODE_ALLOWED;
450     }
451 
checkAppOpAllowed(String op, String pkgName, int uid)452     private boolean checkAppOpAllowed(String op, String pkgName, int uid) {
453         return mAppOps.unsafeCheckOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED;
454     }
455 
retrieveLocationManagerIfNecessary()456     private boolean retrieveLocationManagerIfNecessary() {
457         // This is going to be accessed by multiple threads.
458         synchronized (mLock) {
459             if (mLocationManager == null) {
460                 mLocationManager =
461                         (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
462             }
463         }
464         return mLocationManager != null;
465     }
466 
467     /**
468      * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
469      */
isLocationModeEnabled()470     public boolean isLocationModeEnabled() {
471         if (!retrieveLocationManagerIfNecessary()) return false;
472         try {
473             return mLocationManager.isLocationEnabledForUser(UserHandle.of(
474                     mWifiPermissionsWrapper.getCurrentUser()));
475         } catch (Exception e) {
476             Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
477             return mFrameworkFacade.getIntegerSetting(
478                     mContext, Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
479                     == Settings.Secure.LOCATION_MODE_ON;
480         }
481     }
482 
483     /**
484      * Returns true if the |uid| holds NETWORK_SETTINGS permission.
485      */
checkNetworkSettingsPermission(int uid)486     public boolean checkNetworkSettingsPermission(int uid) {
487         return mWifiPermissionsWrapper.getUidPermission(
488                 android.Manifest.permission.NETWORK_SETTINGS, uid)
489                 == PackageManager.PERMISSION_GRANTED;
490     }
491 
492     /**
493      * Returns true if the |uid| holds RADIO_SCAN_WITHOUT_LOCATION permission.
494      */
checkScanWithoutLocationPermission(int uid)495     public boolean checkScanWithoutLocationPermission(int uid) {
496         return mWifiPermissionsWrapper.getUidPermission(
497                 android.Manifest.permission.RADIO_SCAN_WITHOUT_LOCATION, uid)
498                 == PackageManager.PERMISSION_GRANTED;
499     }
500 
501     /**
502      * Returns true if the |uid| holds LOCAL_MAC_ADDRESS permission.
503      */
checkLocalMacAddressPermission(int uid)504     public boolean checkLocalMacAddressPermission(int uid) {
505         return mWifiPermissionsWrapper.getUidPermission(
506                 android.Manifest.permission.LOCAL_MAC_ADDRESS, uid)
507                 == PackageManager.PERMISSION_GRANTED;
508     }
509 
510     /**
511      * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
512      */
checkNetworkSetupWizardPermission(int uid)513     public boolean checkNetworkSetupWizardPermission(int uid) {
514         return mWifiPermissionsWrapper.getUidPermission(
515                 android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
516                 == PackageManager.PERMISSION_GRANTED;
517     }
518 
519     /**
520      * Returns true if the |uid| holds NETWORK_STACK permission.
521      */
checkNetworkStackPermission(int uid)522     public boolean checkNetworkStackPermission(int uid) {
523         return mWifiPermissionsWrapper.getUidPermission(
524                 android.Manifest.permission.NETWORK_STACK, uid)
525                 == PackageManager.PERMISSION_GRANTED;
526     }
527 
528     /**
529      * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
530      */
checkMainlineNetworkStackPermission(int uid)531     public boolean checkMainlineNetworkStackPermission(int uid) {
532         return mWifiPermissionsWrapper.getUidPermission(
533                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
534                 == PackageManager.PERMISSION_GRANTED;
535     }
536 
537     /**
538      * Returns true if the |uid| holds NETWORK_MANAGED_PROVISIONING permission.
539      */
checkNetworkManagedProvisioningPermission(int uid)540     public boolean checkNetworkManagedProvisioningPermission(int uid) {
541         return mWifiPermissionsWrapper.getUidPermission(
542                 android.Manifest.permission.NETWORK_MANAGED_PROVISIONING, uid)
543                 == PackageManager.PERMISSION_GRANTED;
544     }
545 
546     /**
547      * Returns true if the |uid| holds NETWORK_CARRIER_PROVISIONING permission.
548      */
checkNetworkCarrierProvisioningPermission(int uid)549     public boolean checkNetworkCarrierProvisioningPermission(int uid) {
550         return mWifiPermissionsWrapper.getUidPermission(
551                 android.Manifest.permission.NETWORK_CARRIER_PROVISIONING, uid)
552                 == PackageManager.PERMISSION_GRANTED;
553     }
554 
555     /**
556      * Returns true if the |uid| holds READ_WIFI_CREDENTIAL permission.
557      */
checkReadWifiCredentialPermission(int uid)558     public boolean checkReadWifiCredentialPermission(int uid) {
559         return mWifiPermissionsWrapper.getUidPermission(
560                 android.Manifest.permission.READ_WIFI_CREDENTIAL, uid)
561                 == PackageManager.PERMISSION_GRANTED;
562     }
563 
564     /**
565      * Returns true if the |callingUid|/\callingPackage| holds SYSTEM_ALERT_WINDOW permission.
566      */
checkSystemAlertWindowPermission(int callingUid, String callingPackage)567     public boolean checkSystemAlertWindowPermission(int callingUid, String callingPackage) {
568         final int mode = mAppOps.noteOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, callingUid,
569                 callingPackage, null, null);
570         if (mode == AppOpsManager.MODE_DEFAULT) {
571             return mWifiPermissionsWrapper.getUidPermission(
572                     Manifest.permission.SYSTEM_ALERT_WINDOW, callingUid)
573                     == PackageManager.PERMISSION_GRANTED;
574         }
575         return mode == AppOpsManager.MODE_ALLOWED;
576     }
577 
retrieveDevicePolicyManagerFromContext(Context context)578     private static DevicePolicyManager retrieveDevicePolicyManagerFromContext(Context context) {
579         DevicePolicyManager devicePolicyManager =
580                 context.getSystemService(DevicePolicyManager.class);
581         if (devicePolicyManager == null
582                 && context.getPackageManager().hasSystemFeature(
583                 PackageManager.FEATURE_DEVICE_ADMIN)) {
584             Log.w(TAG, "Error retrieving DPM service");
585         }
586         return devicePolicyManager;
587     }
588 
589     @Nullable
createPackageContextAsUser(int uid)590     private Context createPackageContextAsUser(int uid) {
591         Context userContext = null;
592         try {
593             userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
594                     UserHandle.getUserHandleForUid(uid));
595         } catch (PackageManager.NameNotFoundException e) {
596             Log.e(TAG, "Unknown package name");
597             return null;
598         }
599         if (userContext == null) {
600             Log.e(TAG, "Unable to retrieve user context for " + uid);
601             return null;
602         }
603         return userContext;
604     }
605 
retrieveDevicePolicyManagerFromUserContext(int uid)606     private DevicePolicyManager retrieveDevicePolicyManagerFromUserContext(int uid) {
607         Context userContext = createPackageContextAsUser(uid);
608         if (userContext == null) return null;
609         return retrieveDevicePolicyManagerFromContext(userContext);
610     }
611 
612     @Nullable
getDeviceOwner()613     private Pair<UserHandle, ComponentName> getDeviceOwner() {
614         DevicePolicyManager devicePolicyManager =
615                 retrieveDevicePolicyManagerFromContext(mContext);
616         if (devicePolicyManager == null) return null;
617         long ident = Binder.clearCallingIdentity();
618         UserHandle deviceOwnerUser = null;
619         ComponentName deviceOwnerComponent = null;
620         try {
621             deviceOwnerUser = devicePolicyManager.getDeviceOwnerUser();
622             deviceOwnerComponent = devicePolicyManager.getDeviceOwnerComponentOnAnyUser();
623         } finally {
624             Binder.restoreCallingIdentity(ident);
625         }
626         if (deviceOwnerUser == null || deviceOwnerComponent == null) return null;
627 
628         if (deviceOwnerComponent.getPackageName() == null) {
629             // shouldn't happen
630             Log.wtf(TAG, "no package name on device owner component: " + deviceOwnerComponent);
631             return null;
632         }
633         return new Pair<>(deviceOwnerUser, deviceOwnerComponent);
634     }
635 
636     /**
637      * Returns {@code true} if the calling {@code uid} and {@code packageName} is the device owner.
638      */
isDeviceOwner(int uid, @Nullable String packageName)639     public boolean isDeviceOwner(int uid, @Nullable String packageName) {
640         // Cannot determine if the app is DO/PO if packageName is null. So, will return false to be
641         // safe.
642         if (packageName == null) {
643             Log.e(TAG, "isDeviceOwner: packageName is null, returning false");
644             return false;
645         }
646         Pair<UserHandle, ComponentName> deviceOwner = getDeviceOwner();
647         if (mVerboseLoggingEnabled) Log.v(TAG, "deviceOwner:" + deviceOwner);
648 
649         // no device owner
650         if (deviceOwner == null) return false;
651 
652         return deviceOwner.first.equals(UserHandle.getUserHandleForUid(uid))
653                 && deviceOwner.second.getPackageName().equals(packageName);
654     }
655 
656     /**
657      * Returns {@code true} if the calling {@code uid} is the device owner.
658      */
isDeviceOwner(int uid)659     public boolean isDeviceOwner(int uid) {
660         Pair<UserHandle, ComponentName> deviceOwner = getDeviceOwner();
661 
662         // no device owner
663         if (deviceOwner == null) return false;
664 
665         // device owner belowngs to wrong user
666         if (!deviceOwner.first.equals(UserHandle.getUserHandleForUid(uid))) return false;
667 
668         // finally, check uid
669         String deviceOwnerPackageName = deviceOwner.second.getPackageName();
670         String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
671         if (mVerboseLoggingEnabled) {
672             Log.v(TAG, "Packages for uid " + uid + ":" + Arrays.toString(packageNames));
673         }
674         if (packageNames == null) {
675             Log.w(TAG, "isDeviceOwner(): could not find packages for packageName="
676                     + deviceOwnerPackageName + " uid=" + uid);
677             return false;
678         }
679         for (String packageName : packageNames) {
680             if (deviceOwnerPackageName.equals(packageName)) return true;
681         }
682 
683         return false;
684     }
685 
686     /**
687      * Returns true if the |callingUid|/\callingPackage| is the profile owner.
688      */
isProfileOwner(int uid, @Nullable String packageName)689     public boolean isProfileOwner(int uid, @Nullable String packageName) {
690         // Cannot determine if the app is DO/PO if packageName is null. So, will return false to be
691         // safe.
692         if (packageName == null) {
693             Log.e(TAG, "isProfileOwner: packageName is null, returning false");
694             return false;
695         }
696         DevicePolicyManager devicePolicyManager =
697                 retrieveDevicePolicyManagerFromUserContext(uid);
698         if (devicePolicyManager == null) return false;
699         return devicePolicyManager.isProfileOwnerApp(packageName);
700     }
701 
702     /** Helper method to check if the entity initiating the binder call is a system app. */
isSystem(String packageName, int uid)703     public boolean isSystem(String packageName, int uid) {
704         long ident = Binder.clearCallingIdentity();
705         try {
706             ApplicationInfo info = mContext.getPackageManager().getApplicationInfoAsUser(
707                     packageName, 0, UserHandle.getUserHandleForUid(uid));
708             return (info.flags & APP_INFO_FLAGS_SYSTEM_APP) != 0;
709         } catch (PackageManager.NameNotFoundException e) {
710             // In case of exception, assume unknown app (more strict checking)
711             // Note: This case will never happen since checkPackage is
712             // called to verify validity before checking App's version.
713         } finally {
714             Binder.restoreCallingIdentity(ident);
715         }
716         return false;
717     }
718 
719     /**
720      * Checks if the given UID belongs to the current foreground or device owner user. This is
721      * used to prevent apps running in background users from modifying network
722      * configurations.
723      * <p>
724      * UIDs belonging to system internals (such as SystemUI) are always allowed,
725      * since they always run as {@link UserHandle#USER_SYSTEM}.
726      *
727      * @param uid uid of the app.
728      * @return true if the given UID belongs to the current foreground user,
729      *         otherwise false.
730      */
doesUidBelongToCurrentUser(int uid)731     public boolean doesUidBelongToCurrentUser(int uid) {
732         if (uid == android.os.Process.SYSTEM_UID
733                 // UIDs with the NETWORK_SETTINGS permission are always allowed since they are
734                 // acting on behalf of the user.
735                 || checkNetworkSettingsPermission(uid)) {
736             return true;
737         }
738         boolean isCurrentProfile = isCurrentProfile(uid);
739         if (!isCurrentProfile) {
740             // Fix for b/174749461
741             EventLog.writeEvent(0x534e4554, "174749461", -1,
742                     "Non foreground user trying to modify wifi configuration");
743         }
744         return isCurrentProfile || isDeviceOwner(uid);
745     }
746 
747     /**
748      * Sets the verbose logging level.
749      */
enableVerboseLogging(boolean enabled)750     public void enableVerboseLogging(boolean enabled) {
751         mVerboseLoggingEnabled = enabled;
752     }
753 }
754