• 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 static android.Manifest.permission.ACCESS_COARSE_LOCATION;
20 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
21 import static android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED;
22 import static android.Manifest.permission.NEARBY_WIFI_DEVICES;
23 import static android.Manifest.permission.RENOUNCE_PERMISSIONS;
24 import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION;
25 import static android.content.pm.PackageManager.GET_PERMISSIONS;
26 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
27 
28 import android.Manifest;
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.app.AppOpsManager;
32 import android.app.admin.DevicePolicyManager;
33 import android.app.admin.WifiSsidPolicy;
34 import android.content.AttributionSource;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.pm.ApplicationInfo;
38 import android.content.pm.PackageInfo;
39 import android.content.pm.PackageManager;
40 import android.location.LocationManager;
41 import android.net.NetworkStack;
42 import android.net.wifi.SecurityParams;
43 import android.net.wifi.WifiConfiguration;
44 import android.net.wifi.WifiInfo;
45 import android.net.wifi.WifiSsid;
46 import android.os.Binder;
47 import android.os.Build;
48 import android.os.Process;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.permission.PermissionManager;
52 import android.provider.Settings;
53 import android.util.ArraySet;
54 import android.util.EventLog;
55 import android.util.Log;
56 import android.util.Pair;
57 import android.util.SparseBooleanArray;
58 
59 import androidx.annotation.RequiresApi;
60 
61 import com.android.internal.annotations.GuardedBy;
62 import com.android.modules.utils.build.SdkLevel;
63 import com.android.server.wifi.FrameworkFacade;
64 import com.android.server.wifi.WifiInjector;
65 import com.android.server.wifi.WifiLog;
66 import com.android.wifi.resources.R;
67 
68 import java.util.Arrays;
69 import java.util.Set;
70 
71 /**
72  * A wifi permissions utility assessing permissions
73  * for getting scan results by a package.
74  */
75 public class WifiPermissionsUtil {
76     private static final String TAG = "WifiPermissionsUtil";
77 
78     private static final int APP_INFO_FLAGS_SYSTEM_APP =
79             ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
80     private final WifiPermissionsWrapper mWifiPermissionsWrapper;
81     private final Context mContext;
82     private final FrameworkFacade mFrameworkFacade;
83     private final AppOpsManager mAppOps;
84     private final UserManager mUserManager;
85     private final PermissionManager mPermissionManager;
86     private final Object mLock = new Object();
87     @GuardedBy("mLock")
88     private LocationManager mLocationManager;
89     private WifiLog mLog;
90     private boolean mVerboseLoggingEnabled;
91     private final SparseBooleanArray mOemPrivilegedAdminUidCache = new SparseBooleanArray();
92 
WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper, Context context, UserManager userManager, WifiInjector wifiInjector)93     public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper,
94             Context context, UserManager userManager, WifiInjector wifiInjector) {
95         mWifiPermissionsWrapper = wifiPermissionsWrapper;
96         mContext = context;
97         mFrameworkFacade = wifiInjector.getFrameworkFacade();
98         mUserManager = userManager;
99         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
100         mPermissionManager = mContext.getSystemService(PermissionManager.class);
101         mLog = wifiInjector.makeLog(TAG);
102     }
103 
104 
105     /**
106      * A class to store binder caller information.
107      */
108     public static final class CallerIdentity {
109         int mUid;
110         int mPid;
111         String mPackageName;
112         String mFeatureId;
113 
CallerIdentity(int uid, int pid, String packageName, String featureId)114         public CallerIdentity(int uid, int pid, String packageName, String featureId) {
115             mUid = uid;
116             mPid = pid;
117             mPackageName = packageName;
118             mFeatureId = featureId;
119         }
120 
getUid()121         public int getUid() {
122             return mUid;
123         }
124 
getPid()125         public int getPid() {
126             return mPid;
127         }
128 
getPackageName()129         public String getPackageName() {
130             return mPackageName;
131         }
132 
getFeatureId()133         public String getFeatureId() {
134             return mFeatureId;
135         }
136 
137         @NonNull
138         @Override
toString()139         public String toString() {
140             return "CallerIdentity{"
141                     + "Uid= " + mUid
142                     + ", Pid= " + mPid
143                     + ", PackageName= " + mPackageName
144                     + ", FeatureId= " + mFeatureId
145                     + '}';
146         }
147     }
148 
149     /**
150      * Checks if the app has the permission to override Wi-Fi network configuration or not.
151      *
152      * @param uid uid of the app.
153      * @return true if the app does have the permission, false otherwise.
154      */
checkConfigOverridePermission(int uid)155     public boolean checkConfigOverridePermission(int uid) {
156         return mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid)
157                 == PackageManager.PERMISSION_GRANTED;
158     }
159 
160     /**
161      * Check and enforce Coarse or Fine Location permission (depending on target SDK).
162      *
163      * @param pkgName PackageName of the application requesting access
164      * @param featureId The feature in the package
165      * @param uid The uid of the package
166      */
enforceLocationPermission(String pkgName, @Nullable String featureId, int uid)167     public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid) {
168         if (!checkCallersLocationPermission(pkgName, featureId,
169                 uid, /* coarseForTargetSdkLessThanQ */ true, null)) {
170             throw new SecurityException(
171                     "UID " + uid + " does not have Coarse/Fine Location permission");
172         }
173     }
174 
175     /**
176      * Version of enforceNearbyDevicesPermission that do not throw an exception.
177      */
checkNearbyDevicesPermission(AttributionSource attributionSource, boolean checkForLocation, String message)178     public boolean checkNearbyDevicesPermission(AttributionSource attributionSource,
179             boolean checkForLocation, String message) {
180         try {
181             enforceNearbyDevicesPermission(attributionSource, checkForLocation, message);
182         } catch (SecurityException e) {
183             return false;
184         }
185         return true;
186     }
187 
188     /**
189      * Check and enforce NEARBY_WIFI_DEVICES permission and optionally enforce for either location
190      * disavowal or location permission.
191      *
192      * Note, this is only callable on SDK version T and later.
193      *
194      * @param attributionSource AttributionSource of the caller.
195      * @param checkForLocation If true will require the caller to either disavow location
196      *                         or actually have location permission.
197      * @param message String to log as the reason for performing permission checks.
198      */
enforceNearbyDevicesPermission(AttributionSource attributionSource, boolean checkForLocation, String message)199     public void enforceNearbyDevicesPermission(AttributionSource attributionSource,
200             boolean checkForLocation, String message) throws SecurityException {
201         if (!SdkLevel.isAtLeastT()) {
202             Log.wtf(TAG, "enforceNearbyDevicesPermission should never be called on pre-T "
203                     + "devices");
204             throw new SecurityException("enforceNearbyDevicesPermission requires at least "
205                     + "Android T");
206         }
207         if (attributionSource == null) {
208             throw new SecurityException("enforceNearbyDevicesPermission attributionSource is null");
209         }
210         if (mVerboseLoggingEnabled) {
211             Log.v(TAG, "enforceNearbyDevicesPermission(attributionSource="
212                     + attributionSource + ", checkForLocation=" + checkForLocation);
213         }
214         if (!attributionSource.checkCallingUid()) {
215             throw new SecurityException("enforceNearbyDevicesPermission invalid attribution source="
216                     + attributionSource);
217         }
218         String packageName = attributionSource.getPackageName();
219         int uid = attributionSource.getUid();
220         checkPackage(uid, packageName);
221         // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_MANAGED_PROVISIONING,
222         // NETWORK_STACK & MAINLINE_NETWORK_STACK, RADIO_SCAN_WITHOUT_LOCATION are granted a bypass.
223         if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
224                 || checkNetworkManagedProvisioningPermission(uid)
225                 || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)
226                 || checkScanWithoutLocationPermission(uid)) {
227             return;
228         }
229 
230         int permissionCheckResult = mPermissionManager.checkPermissionForDataDelivery(
231                 Manifest.permission.NEARBY_WIFI_DEVICES, attributionSource, message);
232         if (permissionCheckResult != PermissionManager.PERMISSION_GRANTED) {
233             throw new SecurityException("package=" + packageName + " UID=" + uid
234                     + " does not have nearby devices permission.");
235         }
236         if (mVerboseLoggingEnabled) {
237             Log.v(TAG, "pkg=" + packageName + " has NEARBY_WIFI_DEVICES permission.");
238         }
239         if (!checkForLocation) {
240             // No need to check for location permission. All done now and return.
241             return;
242         }
243 
244         // There are 2 ways to disavow location. Skip location permission check if any of the
245         // 2 ways are used to disavow location usage.
246         // First check if the app renounced location.
247         // Check every step along the attribution chain for a renouncement.
248         AttributionSource currentAttrib = attributionSource;
249         while (true) {
250             int curUid = currentAttrib.getUid();
251             String curPackageName = currentAttrib.getPackageName();
252             // If location has been renounced anywhere in the chain we treat it as a disavowal.
253             if (currentAttrib.getRenouncedPermissions().contains(ACCESS_FINE_LOCATION)
254                     && mWifiPermissionsWrapper.getUidPermission(RENOUNCE_PERMISSIONS, curUid)
255                     == PackageManager.PERMISSION_GRANTED) {
256                 if (mVerboseLoggingEnabled) {
257                     Log.v(TAG, "package=" + curPackageName + " UID=" + curUid
258                             + " has renounced location permission - bypassing location check.");
259                 }
260                 return;
261             }
262             AttributionSource nextAttrib = currentAttrib.getNext();
263             if (nextAttrib == null) {
264                 break;
265             }
266             currentAttrib = nextAttrib;
267         }
268         // If the app did not renounce location, check if "neverForLocation" is set.
269         PackageManager pm = mContext.getPackageManager();
270         long ident = Binder.clearCallingIdentity();
271         try {
272             PackageInfo pkgInfo = pm.getPackageInfo(packageName,
273                     GET_PERMISSIONS | MATCH_UNINSTALLED_PACKAGES);
274             int requestedPermissionsLength = pkgInfo.requestedPermissions == null
275                     || pkgInfo.requestedPermissionsFlags == null ? 0
276                     : pkgInfo.requestedPermissions.length;
277             if (requestedPermissionsLength == 0) {
278                 Log.e(TAG, "package=" + packageName + " unexpectedly has null "
279                         + "requestedPermissions or requestPermissionFlags.");
280             }
281             for (int i = 0; i < requestedPermissionsLength; i++) {
282                 if (pkgInfo.requestedPermissions[i].equals(NEARBY_WIFI_DEVICES)
283                         && (pkgInfo.requestedPermissionsFlags[i]
284                         & PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION) != 0) {
285                     if (mVerboseLoggingEnabled) {
286                         Log.v(TAG, "package=" + packageName + " UID=" + uid
287                                 + " has declared neverForLocation - bypassing location check.");
288                     }
289                     return;
290                 }
291             }
292         } catch (PackageManager.NameNotFoundException e) {
293             Log.w(TAG, "Could not find package for disavowal check: " + packageName);
294         } finally {
295             Binder.restoreCallingIdentity(ident);
296         }
297         // App did not disavow location. Check for location permission and location mode.
298         ident = Binder.clearCallingIdentity();
299         try {
300             if (!isLocationModeEnabled()) {
301                 if (mVerboseLoggingEnabled) {
302                     Log.v(TAG, "enforceNearbyDevicesPermission(pkg=" + packageName + ", uid=" + uid
303                             + "): "
304                             + "location is disabled");
305                 }
306                 throw new SecurityException("Location mode is disabled for the device");
307             }
308         } finally {
309             Binder.restoreCallingIdentity(ident);
310         }
311         if (mPermissionManager.checkPermissionForDataDelivery(
312                 ACCESS_FINE_LOCATION, attributionSource, message)
313                 == PermissionManager.PERMISSION_GRANTED) {
314             if (mVerboseLoggingEnabled) {
315                 Log.v(TAG, "package=" + packageName + " UID=" + uid + " has location permission.");
316             }
317             return;
318         }
319         throw new SecurityException("package=" + packageName + ", UID=" + uid
320                 + " does not have Fine Location permission");
321     }
322 
323     /**
324      * Checks whether than the target SDK of the package is less than the specified version code.
325      */
isTargetSdkLessThan(String packageName, int versionCode, int callingUid)326     public boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
327         long ident = Binder.clearCallingIdentity();
328         try {
329             final int targetSdkVersion;
330             if (SdkLevel.isAtLeastS()) {
331                 // >= S, use the lightweight API to just get the target SDK version.
332                 Context userContext = createPackageContextAsUser(callingUid);
333                 if (userContext == null) return false;
334                 targetSdkVersion = userContext.getPackageManager().getTargetSdkVersion(packageName);
335             } else {
336                 // < S, use the heavyweight API to get all package info.
337                 targetSdkVersion = mContext.getPackageManager().getApplicationInfoAsUser(
338                         packageName, 0,
339                         UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion;
340             }
341             return targetSdkVersion < versionCode;
342         } catch (PackageManager.NameNotFoundException e) {
343             // In case of exception, assume unknown app (more strict checking)
344             // Note: This case will never happen since checkPackage is
345             // called to verify validity before checking App's version.
346             return false;
347         } finally {
348             Binder.restoreCallingIdentity(ident);
349         }
350     }
351 
352     /**
353      * Returns the global demo mode of the device. Note that there is a
354      * UserManager.isDeviceInDemoMode(Context) which does the same thing - but is not a
355      * public/system API (whereas the Settings.Global.DEVICE_DEMO_MODE is a System API).
356      */
isDeviceInDemoMode(Context context)357     public boolean isDeviceInDemoMode(Context context) {
358         return Settings.Global.getInt(context.getContentResolver(),
359                 Settings.Global.DEVICE_DEMO_MODE, 0) > 0;
360     }
361 
362     /**
363      * Check and enforce Location permission in the manifest.
364      *
365      * @param uid uid of the app.
366      * @param isCoarseOnly whether permission type is COARSE or FINE since FINE permission
367      *                     implies having COARSE permission.
368      */
enforceLocationPermissionInManifest(int uid, boolean isCoarseOnly)369     public void enforceLocationPermissionInManifest(int uid, boolean isCoarseOnly) {
370         if (!checkCallersLocationPermissionInManifest(uid, isCoarseOnly)) {
371             throw new SecurityException("UID " + uid + " does not have Location permission ("
372                     + "isCoarseOnly = " + isCoarseOnly + " )");
373         }
374     }
375 
376     /**
377      * Checks if the app has the location permission in the manifest.
378      *
379      * @param uid uid of the app.
380      * @param isCoarseOnly whether permission type is COARSE or FINE since FINE permission
381      *                     implies having COARSE permission.
382      * @return true if the app does have the permission, false otherwise.
383      */
checkCallersLocationPermissionInManifest(int uid, boolean isCoarseOnly)384     public boolean checkCallersLocationPermissionInManifest(int uid, boolean isCoarseOnly) {
385         // Having FINE permission implies having COARSE permission (but not the reverse)
386         String permissionType = isCoarseOnly ? ACCESS_COARSE_LOCATION : ACCESS_FINE_LOCATION;
387         return mWifiPermissionsWrapper.getUidPermission(permissionType, uid)
388                 == PackageManager.PERMISSION_GRANTED;
389     }
390 
391     /**
392      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
393      * android.Manifest.permission.ACCESS_FINE_LOCATION (depending on config/targetSDK leve)
394      * and a corresponding app op is allowed for this package and uid.
395      *
396      * @param pkgName PackageName of the application requesting access
397      * @param featureId The feature in the package
398      * @param uid The uid of the package
399      * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
400      *                                    else (false or targetSDK >= Q) then will check for FINE
401      * @param message A message describing why the permission was checked. Only needed if this is
402      *                not inside of a two-way binder call from the data receiver
403      */
checkCallersLocationPermission(String pkgName, @Nullable String featureId, int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message)404     public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
405             int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
406         boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
407 
408         String permissionType = ACCESS_FINE_LOCATION;
409         if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
410             // Having FINE permission implies having COARSE permission (but not the reverse)
411             permissionType = ACCESS_COARSE_LOCATION;
412         }
413         if (mWifiPermissionsWrapper.getUidPermission(permissionType, uid)
414                 == PackageManager.PERMISSION_DENIED) {
415             if (mVerboseLoggingEnabled) {
416                 Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): uid " + uid
417                         + " doesn't have permission " + permissionType);
418             }
419             return false;
420         }
421 
422         // Always checking FINE - even if will not enforce. This will record the request for FINE
423         // so that a location request by the app is surfaced to the user.
424         boolean isFineLocationAllowed = noteAppOpAllowed(
425                 AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
426         if (isFineLocationAllowed) {
427             if (mVerboseLoggingEnabled) {
428                 Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): ok because uid " + uid
429                         + " has app-op " + AppOpsManager.OPSTR_FINE_LOCATION);
430             }
431             return true;
432         }
433         if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
434             boolean allowed = noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName,
435                     featureId, uid, message);
436             if (mVerboseLoggingEnabled) {
437                 Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): returning " + allowed
438                         + " because uid " + uid + (allowed ? "has" : "doesn't have") + " app-op "
439                         + AppOpsManager.OPSTR_COARSE_LOCATION);
440             }
441             return allowed;
442         }
443         if (mVerboseLoggingEnabled) {
444             Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): returning false for " + uid
445                     + ": coarseForTargetSdkLessThanQ=" + coarseForTargetSdkLessThanQ
446                     + ", isTargetSdkLessThanQ=" + isTargetSdkLessThanQ);
447 
448         }
449         return false;
450     }
451 
452     /**
453      * Check and enforce Fine Location permission.
454      *
455      * @param pkgName PackageName of the application requesting access
456      * @param featureId The feature in the package
457      * @param uid The uid of the package
458      */
enforceFineLocationPermission(String pkgName, @Nullable String featureId, int uid)459     public void enforceFineLocationPermission(String pkgName, @Nullable String featureId,
460             int uid) {
461         if (!checkCallersFineLocationPermission(pkgName, featureId, uid, false, false)) {
462             throw new SecurityException("UID " + uid + " does not have Fine Location permission");
463         }
464     }
465 
466     /**
467      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION
468      * and a corresponding app op is allowed for this package and uid.
469      *
470      * @param pkgName PackageName of the application requesting access
471      * @param featureId The feature in the package
472      * @param uid The uid of the package
473      * @param hideFromAppOps True to invoke {@link AppOpsManager#checkOp(int, int, String)}, false
474      *                       to invoke {@link AppOpsManager#noteOp(String, int, String, String,
475      *                       String)}.
476      * @param ignoreLocationSettings Whether this request can bypass location settings.
477      */
checkCallersFineLocationPermission(String pkgName, @Nullable String featureId, int uid, boolean hideFromAppOps, boolean ignoreLocationSettings)478     private boolean checkCallersFineLocationPermission(String pkgName, @Nullable String featureId,
479             int uid, boolean hideFromAppOps, boolean ignoreLocationSettings) {
480         // Having FINE permission implies having COARSE permission (but not the reverse)
481         if (mWifiPermissionsWrapper.getUidPermission(
482                 ACCESS_FINE_LOCATION, uid)
483                 == PackageManager.PERMISSION_DENIED) {
484             return false;
485         }
486 
487         boolean isAllowed;
488         if (hideFromAppOps) {
489             // Don't note the operation, just check if the app is allowed to perform the operation.
490             isAllowed = checkAppOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION, pkgName, uid);
491         } else {
492             isAllowed = noteAppOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid,
493                     null);
494         }
495         // If the ignoreLocationSettings is true, we always return true. This is for the emergency
496         // location service use case. But still notify the operation manager.
497         return isAllowed || ignoreLocationSettings;
498     }
499 
500     /**
501      * Check and enforce Coarse Location permission.
502      *
503      * @param pkgName PackageName of the application requesting access.
504      * @param featureId The feature in the package.
505      * @param uid The uid of the package.
506      */
enforceCoarseLocationPermission(String pkgName, @Nullable String featureId, int uid)507     public void enforceCoarseLocationPermission(String pkgName, @Nullable String featureId,
508             int uid) {
509         if (!checkCallersCoarseLocationPermission(pkgName, featureId,
510                 uid, null)) {
511             throw new SecurityException(
512                     "UID " + uid + " does not have Coarse Location permission");
513         }
514     }
515 
516     /**
517      * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION
518      * and a corresponding app op is allowed for this package and uid.
519      *
520      * @param pkgName PackageName of the application requesting access.
521      * @param featureId The feature in the package.
522      * @param uid The uid of the package.
523      * @param message A message describing why the permission was checked. Only needed if this is
524      *                not inside of a two-way binder call from the data receiver.
525      */
checkCallersCoarseLocationPermission(String pkgName, @Nullable String featureId, int uid, @Nullable String message)526     public boolean checkCallersCoarseLocationPermission(String pkgName, @Nullable String featureId,
527             int uid, @Nullable String message) {
528         if (mWifiPermissionsWrapper.getUidPermission(ACCESS_COARSE_LOCATION, uid)
529                 == PackageManager.PERMISSION_DENIED) {
530             if (mVerboseLoggingEnabled) {
531                 Log.v(TAG, "checkCallersCoarseLocationPermission(" + pkgName + "): uid " + uid
532                         + " doesn't have ACCESS_COARSE_LOCATION permission ");
533             }
534             return false;
535         }
536         boolean allowed = noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName,
537                     featureId, uid, message);
538         if (mVerboseLoggingEnabled) {
539             Log.v(TAG, "checkCallersCoarseLocationPermission(" + pkgName + "): returning "
540                     + allowed + " because uid " + uid + (allowed ? "has" : "doesn't have")
541                     + " app-op " + AppOpsManager.OPSTR_COARSE_LOCATION);
542         }
543         return allowed;
544     }
545 
546     /**
547      * Checks that calling process has android.Manifest.permission.LOCATION_HARDWARE.
548      *
549      * @param uid The uid of the package
550      */
checkCallersHardwareLocationPermission(int uid)551     public boolean checkCallersHardwareLocationPermission(int uid) {
552         return mWifiPermissionsWrapper.getUidPermission(Manifest.permission.LOCATION_HARDWARE, uid)
553                 == PackageManager.PERMISSION_GRANTED;
554     }
555 
556     /**
557      * API to determine if the caller has permissions to get scan results. Throws SecurityException
558      * if the caller has no permission.
559      * @param pkgName package name of the application requesting access
560      * @param featureId The feature in the package
561      * @param uid The uid of the package
562      * @param message A message describing why the permission was checked. Only needed if this is
563      *                not inside of a two-way binder call from the data receiver
564      */
enforceCanAccessScanResults(String pkgName, @Nullable String featureId, int uid, @Nullable String message)565     public void enforceCanAccessScanResults(String pkgName, @Nullable String featureId, int uid,
566             @Nullable String message)
567             throws SecurityException {
568         checkPackage(uid, pkgName);
569 
570         // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_MANAGED_PROVISIONING,
571         // NETWORK_STACK & MAINLINE_NETWORK_STACK, RADIO_SCAN_WITHOUT_LOCATION are granted a bypass.
572         if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
573                 || checkNetworkManagedProvisioningPermission(uid)
574                 || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)
575                 || checkScanWithoutLocationPermission(uid)) {
576             return;
577         }
578 
579         // Location mode must be enabled
580         if (!isLocationModeEnabled()) {
581             if (mVerboseLoggingEnabled) {
582                 Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
583                         + "location is disabled");
584             }
585             // Location mode is disabled, scan results cannot be returned
586             throw new SecurityException("Location mode is disabled for the device");
587         }
588 
589         // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS permission.
590         boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid);
591         // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
592         // location information.
593         boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId,
594                 uid, /* coarseForTargetSdkLessThanQ */ true, message);
595 
596         // If neither caller or app has location access, there is no need to check
597         // any other permissions. Deny access to scan results.
598         if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
599             if (mVerboseLoggingEnabled) {
600                 Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
601                         + "canCallingUidAccessLocation=" + canCallingUidAccessLocation
602                         + ", canAppPackageUseLocation=" + canAppPackageUseLocation);
603             }
604             throw new SecurityException("UID " + uid + " has no location permission");
605         }
606         // Check if Wifi Scan request is an operation allowed for this App.
607         if (!isScanAllowedbyApps(pkgName, featureId, uid)) {
608             if (mVerboseLoggingEnabled) {
609                 Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
610                         + "doesn't have app-op " + AppOpsManager.OPSTR_WIFI_SCAN);
611             }
612             throw new SecurityException("UID " + uid + " has no wifi scan permission");
613         }
614         // If the User or profile is current, permission is granted
615         // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
616         boolean isCurrentProfile = doesUidBelongToUser(
617                 uid, mWifiPermissionsWrapper.getCurrentUser());
618         if (!isCurrentProfile && !checkInteractAcrossUsersFull(uid)) {
619             if (mVerboseLoggingEnabled) {
620                 Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
621                         + "isCurrentProfile=" + isCurrentProfile
622                         + ", checkInteractAcrossUsersFull=" + checkInteractAcrossUsersFull(uid));
623             }
624             throw new SecurityException("UID " + uid + " profile not permitted");
625         }
626     }
627 
628     /**
629      * API to determine if the caller has permissions to get scan results. Throws SecurityException
630      * if the caller has no permission.
631      * @param pkgName package name of the application requesting access
632      * @param featureId The feature in the package
633      * @param uid The uid of the package
634      * @param ignoreLocationSettings Whether this request can bypass location settings.
635      * @param hideFromAppOps Whether to note the request in app-ops logging or not.
636      *
637      * Note: This is to be used for checking permissions in the internal WifiScanner API surface
638      * for requests coming from system apps.
639      */
enforceCanAccessScanResultsForWifiScanner(String pkgName, @Nullable String featureId, int uid, boolean ignoreLocationSettings, boolean hideFromAppOps)640     public void enforceCanAccessScanResultsForWifiScanner(String pkgName,
641             @Nullable String featureId, int uid, boolean ignoreLocationSettings,
642             boolean hideFromAppOps) throws SecurityException {
643         checkPackage(uid, pkgName);
644 
645         // Apps with NETWORK_SETTINGS or NETWORK_SETUP_WIZARD get a bypass
646         if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)) {
647             return;
648         }
649 
650         // Location mode must be enabled
651         if (!isLocationModeEnabled()) {
652             if (ignoreLocationSettings) {
653                 mLog.w("Request from " + pkgName + " violated location settings");
654             } else {
655                 // Location mode is disabled, scan results cannot be returned
656                 throw new SecurityException("Location mode is disabled for the device");
657             }
658         }
659         // LocationAccess by App: caller must have fine & hardware Location permission to have
660         // access to location information.
661         if (!checkCallersFineLocationPermission(pkgName, featureId, uid, hideFromAppOps,
662                 ignoreLocationSettings) || !checkCallersHardwareLocationPermission(uid)) {
663             throw new SecurityException("UID " + uid + " has no location permission");
664         }
665         // Check if Wifi Scan request is an operation allowed for this App.
666         if (!isScanAllowedbyApps(pkgName, featureId, uid)) {
667             throw new SecurityException("UID " + uid + " has no wifi scan permission");
668         }
669     }
670 
671     /**
672      *
673      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION
674      * and a corresponding app op is allowed for this package and uid
675      *
676      * @param pkgName package name of the application requesting access
677      * @param featureId The feature in the package
678      * @param uid The uid of the package
679      * @param needLocationModeEnabled indicates location mode must be enabled.
680      *
681      * @return true if caller has permission, false otherwise
682      */
checkCanAccessWifiDirect(String pkgName, @Nullable String featureId, int uid, boolean needLocationModeEnabled)683     public boolean checkCanAccessWifiDirect(String pkgName, @Nullable String featureId, int uid,
684                                             boolean needLocationModeEnabled) {
685         try {
686             checkPackage(uid, pkgName);
687         } catch (SecurityException se) {
688             Log.e(TAG, "Package check exception - " + se);
689             return false;
690         }
691 
692         // Apps with NETWORK_SETTINGS are granted a bypass.
693         if (checkNetworkSettingsPermission(uid)) {
694             return true;
695         }
696 
697         // Location mode must be enabled if needed.
698         if (needLocationModeEnabled && !isLocationModeEnabled()) {
699             Log.e(TAG, "Location mode is disabled for the device");
700             return false;
701         }
702 
703         // LocationAccess by App: caller must have Fine Location permission to have access to
704         // location information.
705         if (!checkCallersLocationPermission(pkgName, featureId, uid,
706                 /* coarseForTargetSdkLessThanQ */ false, null)) {
707             Log.e(TAG, "UID " + uid + " has no location permission");
708             return false;
709         }
710         return true;
711     }
712 
713     /**
714      * API to validate if a package name belongs to a UID. Throws SecurityException
715      * if pkgName does not belongs to a UID
716      *
717      * @param pkgName package name of the application requesting access
718      * @param uid The uid of the package
719      *
720      */
checkPackage(int uid, String pkgName)721     public void checkPackage(int uid, String pkgName) throws SecurityException {
722         if (pkgName == null) {
723             throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
724         }
725         mAppOps.checkPackage(uid, pkgName);
726     }
727 
728     /**
729      * Returns true if the caller holds PEERS_MAC_ADDRESS permission.
730      */
checkCallerHasPeersMacAddressPermission(int uid)731     private boolean checkCallerHasPeersMacAddressPermission(int uid) {
732         return mWifiPermissionsWrapper.getUidPermission(
733                 android.Manifest.permission.PEERS_MAC_ADDRESS, uid)
734                 == PackageManager.PERMISSION_GRANTED;
735     }
736 
737     /**
738      * Returns true if Wifi scan operation is allowed for this caller
739      * and package.
740      */
isScanAllowedbyApps(String pkgName, @Nullable String featureId, int uid)741     private boolean isScanAllowedbyApps(String pkgName, @Nullable String featureId, int uid) {
742         return noteAppOpAllowed(AppOpsManager.OPSTR_WIFI_SCAN, pkgName, featureId, uid, null);
743     }
744 
745     /**
746      * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
747      */
checkInteractAcrossUsersFull(int uid)748     private boolean checkInteractAcrossUsersFull(int uid) {
749         return mWifiPermissionsWrapper.getUidPermission(
750                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
751                 == PackageManager.PERMISSION_GRANTED;
752     }
753 
noteAppOpAllowed(String op, String pkgName, @Nullable String featureId, int uid, @Nullable String message)754     private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
755             int uid, @Nullable String message) {
756         return mAppOps.noteOp(op, uid, pkgName, featureId, message) == AppOpsManager.MODE_ALLOWED;
757     }
758 
checkAppOpAllowed(String op, String pkgName, int uid)759     private boolean checkAppOpAllowed(String op, String pkgName, int uid) {
760         return mAppOps.unsafeCheckOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED;
761     }
762 
retrieveLocationManagerIfNecessary()763     private boolean retrieveLocationManagerIfNecessary() {
764         // This is going to be accessed by multiple threads.
765         synchronized (mLock) {
766             if (mLocationManager == null) {
767                 mLocationManager =
768                         (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
769             }
770         }
771         return mLocationManager != null;
772     }
773 
774     /**
775      * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
776      */
isLocationModeEnabled()777     public boolean isLocationModeEnabled() {
778         if (!retrieveLocationManagerIfNecessary()) return false;
779         try {
780             return mLocationManager.isLocationEnabledForUser(UserHandle.of(
781                     mWifiPermissionsWrapper.getCurrentUser()));
782         } catch (Exception e) {
783             Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
784             return mFrameworkFacade.getIntegerSetting(
785                     mContext, Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
786                     == Settings.Secure.LOCATION_MODE_ON;
787         }
788     }
789 
790     /**
791      * Returns true if the |uid| holds REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION permission.
792      */
checkRequestCompanionProfileAutomotiveProjectionPermission(int uid)793     public boolean checkRequestCompanionProfileAutomotiveProjectionPermission(int uid) {
794         return mWifiPermissionsWrapper.getUidPermission(
795                 REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION, uid)
796                 == PackageManager.PERMISSION_GRANTED;
797     }
798 
799     /**
800      * Returns true if the |uid| holds ENTER_CAR_MODE_PRIORITIZED permission.
801      */
checkEnterCarModePrioritized(int uid)802     public boolean checkEnterCarModePrioritized(int uid) {
803         return mWifiPermissionsWrapper.getUidPermission(ENTER_CAR_MODE_PRIORITIZED, uid)
804                 == PackageManager.PERMISSION_GRANTED;
805     }
806 
807     /**
808      * Returns true if the |uid| holds MANAGE_WIFI_INTERFACES permission.
809      */
checkManageWifiInterfacesPermission(int uid)810     public boolean checkManageWifiInterfacesPermission(int uid) {
811         return mWifiPermissionsWrapper.getUidPermission(
812                 android.Manifest.permission.MANAGE_WIFI_INTERFACES, uid)
813                 == PackageManager.PERMISSION_GRANTED;
814     }
815 
816     /**
817      * Returns true if the |uid| holds MANAGE_WIFI_NETWORK_SELECTION permission.
818      */
checkManageWifiNetworkSelectionPermission(int uid)819     public boolean checkManageWifiNetworkSelectionPermission(int uid) {
820         return mWifiPermissionsWrapper.getUidPermission(
821                 android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION, uid)
822                 == PackageManager.PERMISSION_GRANTED;
823     }
824 
825     /**
826      * Returns true if the |uid| holds NETWORK_SETTINGS permission.
827      */
checkNetworkSettingsPermission(int uid)828     public boolean checkNetworkSettingsPermission(int uid) {
829         return mWifiPermissionsWrapper.getUidPermission(
830                 android.Manifest.permission.NETWORK_SETTINGS, uid)
831                 == PackageManager.PERMISSION_GRANTED;
832     }
833 
834     /**
835      * Returns true if the |uid| holds RADIO_SCAN_WITHOUT_LOCATION permission.
836      */
checkScanWithoutLocationPermission(int uid)837     public boolean checkScanWithoutLocationPermission(int uid) {
838         return mWifiPermissionsWrapper.getUidPermission(
839                 android.Manifest.permission.RADIO_SCAN_WITHOUT_LOCATION, uid)
840                 == PackageManager.PERMISSION_GRANTED;
841     }
842 
843     /**
844      * Returns true if the |uid| holds LOCAL_MAC_ADDRESS permission.
845      */
checkLocalMacAddressPermission(int uid)846     public boolean checkLocalMacAddressPermission(int uid) {
847         return mWifiPermissionsWrapper.getUidPermission(
848                 android.Manifest.permission.LOCAL_MAC_ADDRESS, uid)
849                 == PackageManager.PERMISSION_GRANTED;
850     }
851 
852     /**
853      * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
854      */
checkNetworkSetupWizardPermission(int uid)855     public boolean checkNetworkSetupWizardPermission(int uid) {
856         return mWifiPermissionsWrapper.getUidPermission(
857                 android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
858                 == PackageManager.PERMISSION_GRANTED;
859     }
860 
861     /**
862      * Returns true if the |uid| holds NETWORK_STACK permission.
863      */
checkNetworkStackPermission(int uid)864     public boolean checkNetworkStackPermission(int uid) {
865         return mWifiPermissionsWrapper.getUidPermission(
866                 android.Manifest.permission.NETWORK_STACK, uid)
867                 == PackageManager.PERMISSION_GRANTED;
868     }
869 
870     /**
871      * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
872      */
checkMainlineNetworkStackPermission(int uid)873     public boolean checkMainlineNetworkStackPermission(int uid) {
874         return mWifiPermissionsWrapper.getUidPermission(
875                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
876                 == PackageManager.PERMISSION_GRANTED;
877     }
878 
879     /**
880      * Returns true if the |uid| holds NETWORK_MANAGED_PROVISIONING permission.
881      */
checkNetworkManagedProvisioningPermission(int uid)882     public boolean checkNetworkManagedProvisioningPermission(int uid) {
883         return mWifiPermissionsWrapper.getUidPermission(
884                 android.Manifest.permission.NETWORK_MANAGED_PROVISIONING, uid)
885                 == PackageManager.PERMISSION_GRANTED;
886     }
887 
888     /**
889      * Returns true if the |uid| holds NETWORK_CARRIER_PROVISIONING permission.
890      */
checkNetworkCarrierProvisioningPermission(int uid)891     public boolean checkNetworkCarrierProvisioningPermission(int uid) {
892         return mWifiPermissionsWrapper.getUidPermission(
893                 android.Manifest.permission.NETWORK_CARRIER_PROVISIONING, uid)
894                 == PackageManager.PERMISSION_GRANTED;
895     }
896 
897     /**
898      * Returns true if the |uid| holds READ_WIFI_CREDENTIAL permission.
899      */
checkReadWifiCredentialPermission(int uid)900     public boolean checkReadWifiCredentialPermission(int uid) {
901         return mWifiPermissionsWrapper.getUidPermission(
902                 android.Manifest.permission.READ_WIFI_CREDENTIAL, uid)
903                 == PackageManager.PERMISSION_GRANTED;
904     }
905 
906     /**
907      * Returns true if the |uid| holds CAMERA permission.
908      */
checkCameraPermission(int uid)909     public boolean checkCameraPermission(int uid) {
910         return mWifiPermissionsWrapper.getUidPermission(
911                 android.Manifest.permission.CAMERA, uid)
912                 == PackageManager.PERMISSION_GRANTED;
913     }
914 
915     /**
916      * Returns true if the |callingUid|/\callingPackage| holds SYSTEM_ALERT_WINDOW permission.
917      */
checkSystemAlertWindowPermission(int callingUid, String callingPackage)918     public boolean checkSystemAlertWindowPermission(int callingUid, String callingPackage) {
919         final int mode = mAppOps.noteOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, callingUid,
920                 callingPackage, null, null);
921         if (mode == AppOpsManager.MODE_DEFAULT) {
922             return mWifiPermissionsWrapper.getUidPermission(
923                     Manifest.permission.SYSTEM_ALERT_WINDOW, callingUid)
924                     == PackageManager.PERMISSION_GRANTED;
925         }
926         return mode == AppOpsManager.MODE_ALLOWED;
927     }
928 
929     /**
930      * Returns the DevicePolicyManager from context
931      */
retrieveDevicePolicyManagerFromContext(Context context)932     public static DevicePolicyManager retrieveDevicePolicyManagerFromContext(Context context) {
933         DevicePolicyManager devicePolicyManager =
934                 context.getSystemService(DevicePolicyManager.class);
935         if (devicePolicyManager == null
936                 && context.getPackageManager().hasSystemFeature(
937                 PackageManager.FEATURE_DEVICE_ADMIN)) {
938             Log.w(TAG, "Error retrieving DPM service");
939         }
940         return devicePolicyManager;
941     }
942 
943     @Nullable
createPackageContextAsUser(int uid)944     private Context createPackageContextAsUser(int uid) {
945         Context userContext = null;
946         try {
947             userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
948                     UserHandle.getUserHandleForUid(uid));
949         } catch (PackageManager.NameNotFoundException e) {
950             Log.e(TAG, "Unknown package name");
951             return null;
952         }
953         if (userContext == null) {
954             Log.e(TAG, "Unable to retrieve user context for " + uid);
955             return null;
956         }
957         return userContext;
958     }
959 
retrieveDevicePolicyManagerFromUserContext(int uid)960     private DevicePolicyManager retrieveDevicePolicyManagerFromUserContext(int uid) {
961         long ident = Binder.clearCallingIdentity();
962         try {
963             Context userContext = createPackageContextAsUser(uid);
964             if (userContext == null) return null;
965             return retrieveDevicePolicyManagerFromContext(userContext);
966         } finally {
967             Binder.restoreCallingIdentity(ident);
968         }
969     }
970 
971     @Nullable
getDeviceOwner()972     private Pair<UserHandle, ComponentName> getDeviceOwner() {
973         DevicePolicyManager devicePolicyManager =
974                 retrieveDevicePolicyManagerFromContext(mContext);
975         if (devicePolicyManager == null) return null;
976         long ident = Binder.clearCallingIdentity();
977         UserHandle deviceOwnerUser = null;
978         ComponentName deviceOwnerComponent = null;
979         try {
980             deviceOwnerUser = devicePolicyManager.getDeviceOwnerUser();
981             deviceOwnerComponent = devicePolicyManager.getDeviceOwnerComponentOnAnyUser();
982         } finally {
983             Binder.restoreCallingIdentity(ident);
984         }
985         if (deviceOwnerUser == null || deviceOwnerComponent == null) return null;
986 
987         if (deviceOwnerComponent.getPackageName() == null) {
988             // shouldn't happen
989             Log.wtf(TAG, "no package name on device owner component: " + deviceOwnerComponent);
990             return null;
991         }
992         return new Pair<>(deviceOwnerUser, deviceOwnerComponent);
993     }
994 
995     /**
996      * Returns {@code true} if the calling {@code uid} and {@code packageName} is the device owner.
997      */
isDeviceOwner(int uid, @Nullable String packageName)998     public boolean isDeviceOwner(int uid, @Nullable String packageName) {
999         // Cannot determine if the app is DO/PO if packageName is null. So, will return false to be
1000         // safe.
1001         if (packageName == null) {
1002             Log.e(TAG, "isDeviceOwner: packageName is null, returning false");
1003             return false;
1004         }
1005         Pair<UserHandle, ComponentName> deviceOwner = getDeviceOwner();
1006         if (mVerboseLoggingEnabled) Log.v(TAG, "deviceOwner:" + deviceOwner);
1007 
1008         // no device owner
1009         if (deviceOwner == null) return false;
1010 
1011         return deviceOwner.first.equals(UserHandle.getUserHandleForUid(uid))
1012                 && deviceOwner.second.getPackageName().equals(packageName);
1013     }
1014 
1015     /**
1016      * Returns {@code true} if the calling {@code uid} is the device owner.
1017      */
isDeviceOwner(int uid)1018     public boolean isDeviceOwner(int uid) {
1019         Pair<UserHandle, ComponentName> deviceOwner = getDeviceOwner();
1020 
1021         // no device owner
1022         if (deviceOwner == null) return false;
1023 
1024         // device owner belowngs to wrong user
1025         if (!deviceOwner.first.equals(UserHandle.getUserHandleForUid(uid))) return false;
1026 
1027         // finally, check uid
1028         String deviceOwnerPackageName = deviceOwner.second.getPackageName();
1029         String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
1030         if (mVerboseLoggingEnabled) {
1031             Log.v(TAG, "Packages for uid " + uid + ":" + Arrays.toString(packageNames));
1032         }
1033         if (packageNames == null) {
1034             Log.w(TAG, "isDeviceOwner(): could not find packages for packageName="
1035                     + deviceOwnerPackageName + " uid=" + uid);
1036             return false;
1037         }
1038         for (String packageName : packageNames) {
1039             if (deviceOwnerPackageName.equals(packageName)) return true;
1040         }
1041 
1042         return false;
1043     }
1044 
1045     /**
1046      * Returns {@code true} if the calling {@code uid} is the OEM privileged admin.
1047      *
1048      * The admin must be allowlisted in the wifi overlay and signed with system cert.
1049      */
1050     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
isOemPrivilegedAdmin(int uid)1051     public boolean isOemPrivilegedAdmin(int uid) {
1052         synchronized (mOemPrivilegedAdminUidCache) {
1053             int cacheIdx = mOemPrivilegedAdminUidCache.indexOfKey(uid);
1054             if (cacheIdx >= 0) {
1055                 return mOemPrivilegedAdminUidCache.valueAt(cacheIdx);
1056             }
1057         }
1058 
1059         boolean result = isOemPrivilegedAdminNoCache(uid);
1060 
1061         synchronized (mOemPrivilegedAdminUidCache) {
1062             mOemPrivilegedAdminUidCache.put(uid, result);
1063         }
1064 
1065         return result;
1066     }
1067 
1068     /**
1069      * Returns {@code true} if the calling {@code uid} is the OEM privileged admin.
1070      *
1071      * This method doesn't memoize results, use {@code isOemPrivilegedAdmin} instead.
1072      */
1073     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
isOemPrivilegedAdminNoCache(int uid)1074     private boolean isOemPrivilegedAdminNoCache(int uid) {
1075         Set<String> oemPrivilegedAdmins = new ArraySet<>(mContext.getResources()
1076                 .getStringArray(R.array.config_oemPrivilegedWifiAdminPackages));
1077         PackageManager pm = mContext.getPackageManager();
1078         String[] packages = pm.getPackagesForUid(uid);
1079         if (packages == null || Arrays.stream(packages).noneMatch(oemPrivilegedAdmins::contains)) {
1080             return false;
1081         }
1082 
1083         return pm.checkSignatures(uid, Process.SYSTEM_UID) == PackageManager.SIGNATURE_MATCH;
1084     }
1085 
1086     /**
1087      * Returns true if the |callingUid|/|callingPackage| is the profile owner.
1088      */
isProfileOwner(int uid, @Nullable String packageName)1089     public boolean isProfileOwner(int uid, @Nullable String packageName) {
1090         // Cannot determine if the app is DO/PO if packageName is null. So, will return false to be
1091         // safe.
1092         if (packageName == null) {
1093             Log.e(TAG, "isProfileOwner: packageName is null, returning false");
1094             return false;
1095         }
1096         DevicePolicyManager devicePolicyManager =
1097                 retrieveDevicePolicyManagerFromUserContext(uid);
1098         if (devicePolicyManager == null) return false;
1099         return devicePolicyManager.isProfileOwnerApp(packageName);
1100     }
1101 
1102     /**
1103      * Returns {@code true} if the calling {@code uid} is the profile owner of
1104      * an organization owned device.
1105      */
isProfileOwnerOfOrganizationOwnedDevice(int uid)1106     public boolean isProfileOwnerOfOrganizationOwnedDevice(int uid) {
1107         DevicePolicyManager devicePolicyManager =
1108                 retrieveDevicePolicyManagerFromUserContext(uid);
1109         if (devicePolicyManager == null) return false;
1110 
1111         // this relies on having only one PO on COPE device.
1112         if (!devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
1113             return false;
1114         }
1115         String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
1116         if (packages == null) {
1117             Log.w(TAG, "isProfileOwnerOfOrganizationOwnedDevice(): could not find packages for uid="
1118                     + uid);
1119             return false;
1120         }
1121         for (String packageName : packages) {
1122             if (devicePolicyManager.isProfileOwnerApp(packageName)) return true;
1123         }
1124         return false;
1125     }
1126 
1127     /**
1128      * Returns {@code true} if the calling {@code uid} and {@code packageName} is the device owner
1129      * or the profile owner of an organization owned device.
1130      */
isOrganizationOwnedDeviceAdmin(int uid, @Nullable String packageName)1131     public boolean isOrganizationOwnedDeviceAdmin(int uid, @Nullable String packageName) {
1132         boolean isDeviceOwner =
1133                 packageName == null ? isDeviceOwner(uid) : isDeviceOwner(uid, packageName);
1134         return isDeviceOwner || isProfileOwnerOfOrganizationOwnedDevice(uid);
1135     }
1136 
1137     /** Helper method to check if the entity initiating the binder call is a system app. */
isSystem(String packageName, int uid)1138     public boolean isSystem(String packageName, int uid) {
1139         long ident = Binder.clearCallingIdentity();
1140         try {
1141             ApplicationInfo info = mContext.getPackageManager().getApplicationInfoAsUser(
1142                     packageName, 0, UserHandle.getUserHandleForUid(uid));
1143             return (info.flags & APP_INFO_FLAGS_SYSTEM_APP) != 0;
1144         } catch (PackageManager.NameNotFoundException e) {
1145             // In case of exception, assume unknown app (more strict checking)
1146             // Note: This case will never happen since checkPackage is
1147             // called to verify validity before checking App's version.
1148         } finally {
1149             Binder.restoreCallingIdentity(ident);
1150         }
1151         return false;
1152     }
1153 
1154     /**
1155      * Checks if the given UID belongs to the current foreground or device owner user. This is
1156      * used to prevent apps running in background users from modifying network
1157      * configurations.
1158      * <p>
1159      * UIDs belonging to system internals (such as SystemUI) are always allowed,
1160      * since they always run as {@link UserHandle#USER_SYSTEM}.
1161      *
1162      * @param uid uid of the app.
1163      * @return true if the given UID belongs to the current foreground user,
1164      *         otherwise false.
1165      */
doesUidBelongToCurrentUserOrDeviceOwner(int uid)1166     public boolean doesUidBelongToCurrentUserOrDeviceOwner(int uid) {
1167         boolean isCurrentProfile = doesUidBelongToUser(
1168                 uid, mWifiPermissionsWrapper.getCurrentUser());
1169         if (!isCurrentProfile) {
1170             // Fix for b/174749461
1171             EventLog.writeEvent(0x534e4554, "174749461", -1,
1172                     "Non foreground user trying to modify wifi configuration");
1173         }
1174         return isCurrentProfile || isDeviceOwner(uid);
1175     }
1176 
1177     /**
1178      * Check if the current user is a guest user
1179      * @return true if the current user is a guest user, false otherwise.
1180      */
isGuestUser()1181     public boolean isGuestUser() {
1182         UserManager userManager = mContext.createContextAsUser(
1183                 UserHandle.of(mWifiPermissionsWrapper.getCurrentUser()), 0)
1184                 .getSystemService(UserManager.class);
1185         if (userManager == null) {
1186             return true;
1187         }
1188         return userManager.isGuestUser();
1189     }
1190 
1191     /**
1192      * Checks if the given UID belongs to the given user ID. This is
1193      * used to prevent apps running in other users from modifying network configurations belonging
1194      * to the given user.
1195      * <p>
1196      * UIDs belonging to system internals (such as SystemUI) are always allowed,
1197      * since they always run as {@link UserHandle#USER_SYSTEM}.
1198      *
1199      * @param uid uid to check
1200      * @param userId user to check against
1201      * @return true if the given UID belongs to the given user.
1202      */
doesUidBelongToUser(int uid, int userId)1203     public boolean doesUidBelongToUser(int uid, int userId) {
1204         if (UserHandle.getAppId(uid) == android.os.Process.SYSTEM_UID
1205                 // UIDs with the NETWORK_SETTINGS permission are always allowed since they are
1206                 // acting on behalf of the user.
1207                 || checkNetworkSettingsPermission(uid)) {
1208             return true;
1209         }
1210         UserHandle uidHandle = UserHandle.getUserHandleForUid(uid);
1211         UserHandle userHandle = UserHandle.of(userId);
1212         return uidHandle.equals(userHandle)
1213                 || mUserManager.isSameProfileGroup(uidHandle, userHandle);
1214     }
1215 
1216     /**
1217      * Sets the verbose logging level.
1218      */
enableVerboseLogging(boolean enabled)1219     public void enableVerboseLogging(boolean enabled) {
1220         mVerboseLoggingEnabled = enabled;
1221     }
1222 
1223     /**
1224      * Return the corresponding WifiCallerType enum used for WifiStatsLog metrics logging.
1225      */
1226     @RequiresApi(Build.VERSION_CODES.S)
getWifiCallerType(@onNull AttributionSource attributionSource)1227     public int getWifiCallerType(@NonNull AttributionSource attributionSource) {
1228         if (!SdkLevel.isAtLeastS() || attributionSource == null) {
1229             return 0;
1230         }
1231         return getWifiCallerType(attributionSource.getUid(), attributionSource.getPackageName());
1232     }
1233 
1234     /**
1235      * Return the corresponding WifiCallerType enum used for WifiStatsLog metrics logging.
1236      */
getWifiCallerType(int uid, String packageName)1237     public int getWifiCallerType(int uid, String packageName) {
1238         // TODO: Need to hardcode enum values for now since no atom is actually using this enum.
1239         // Once the first atom start using it, replace the hardcoded values with constants generated
1240         // in WifiStatsLog
1241         if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)) {
1242             return 1; // SETTINGS
1243         } else if (isAdmin(uid, packageName)) {
1244             return 2; // ADMIN
1245         } else if (checkEnterCarModePrioritized(uid)) {
1246             return 3; // AUTOMOTIVE
1247         } else if (mContext.getPackageManager().checkSignatures(uid, Process.SYSTEM_UID)
1248                 == PackageManager.SIGNATURE_MATCH) {
1249             return 4; // SIGNATURE
1250         } else if (isSystem(packageName, uid)) {
1251             return 5; // SYSTEM
1252         }
1253         return 6; // OTHERS
1254     }
1255 
1256     /**
1257      * Returns true if the |callingUid|/|callingPackage| is an admin.
1258      */
isAdmin(int uid, @Nullable String packageName)1259     public boolean isAdmin(int uid, @Nullable String packageName) {
1260         // Cannot determine if the app is an admin if packageName is null.
1261         // So, will return false to be safe.
1262         if (packageName == null) {
1263             Log.e(TAG, "isAdmin: packageName is null, returning false");
1264             return false;
1265         }
1266         boolean isOemPrivilegedAdmin = (SdkLevel.isAtLeastT()) ? isOemPrivilegedAdmin(uid) : false;
1267 
1268         return isDeviceOwner(uid, packageName) || isProfileOwner(uid, packageName)
1269                 || isOemPrivilegedAdmin;
1270     }
1271 
1272     /**
1273      * Returns true if a package is a device admin.
1274      * Note that device admin is a deprecated concept so this should only be used in very specific
1275      * cases which require such checks.
1276      */
isLegacyDeviceAdmin(int uid, String packageName)1277     public boolean isLegacyDeviceAdmin(int uid, String packageName) {
1278         if (packageName == null) {
1279             Log.e(TAG, "isLegacyDeviceAdmin: packageName is null, returning false");
1280             return false;
1281         }
1282         DevicePolicyManager devicePolicyManager =
1283                 retrieveDevicePolicyManagerFromUserContext(uid);
1284         if (devicePolicyManager == null) return false;
1285         return devicePolicyManager.packageHasActiveAdmins(packageName);
1286     }
1287 
1288     /**
1289      * Returns true if the device may not connect to the configuration due to admin restriction
1290      */
isAdminRestrictedNetwork(@ullable WifiConfiguration config)1291     public boolean isAdminRestrictedNetwork(@Nullable WifiConfiguration config) {
1292         if (config == null || !SdkLevel.isAtLeastT()) {
1293             return false;
1294         }
1295 
1296         DevicePolicyManager devicePolicyManager =
1297                 WifiPermissionsUtil.retrieveDevicePolicyManagerFromContext(mContext);
1298         if (devicePolicyManager == null) return false;
1299 
1300         int adminMinimumSecurityLevel = 0;
1301         WifiSsidPolicy policy;
1302         long ident = Binder.clearCallingIdentity();
1303         try {
1304             adminMinimumSecurityLevel = devicePolicyManager.getMinimumRequiredWifiSecurityLevel();
1305             policy = devicePolicyManager.getWifiSsidPolicy();
1306         } finally {
1307             Binder.restoreCallingIdentity(ident);
1308         }
1309 
1310         //check minimum security level restriction
1311         if (adminMinimumSecurityLevel != 0) {
1312             boolean securityRestrictionPassed = false;
1313             for (SecurityParams params : config.getSecurityParamsList()) {
1314                 int securityLevel = WifiInfo.convertSecurityTypeToDpmWifiSecurity(
1315                         WifiInfo.convertWifiConfigurationSecurityType(params.getSecurityType()));
1316 
1317                 // Skip unknown security type since security level cannot be determined.
1318                 if (securityLevel == WifiInfo.DPM_SECURITY_TYPE_UNKNOWN) continue;
1319 
1320                 if (adminMinimumSecurityLevel <= securityLevel) {
1321                     securityRestrictionPassed = true;
1322                     break;
1323                 }
1324             }
1325             if (!securityRestrictionPassed) {
1326                 return true;
1327             }
1328         }
1329 
1330         //check SSID restriction
1331         if (policy != null) {
1332             //skip SSID restriction check for Osu and Passpoint networks
1333             if (config.osu || config.isPasspoint()) return false;
1334 
1335             int policyType = policy.getPolicyType();
1336             Set<WifiSsid> ssids = policy.getSsids();
1337             WifiSsid ssid = WifiSsid.fromString(config.SSID);
1338 
1339             if (policyType == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST
1340                     && !ssids.contains(ssid)) {
1341                 return true;
1342             }
1343             if (policyType == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST
1344                     && ssids.contains(ssid)) {
1345                 return true;
1346             }
1347         }
1348         return false;
1349     }
1350 
1351     /**
1352      * Returns the foreground userId
1353      */
getCurrentUser()1354     public int getCurrentUser() {
1355         //set the default to undefined user id (UserHandle.USER_NULL)
1356         int user = -10000;
1357         long ident = Binder.clearCallingIdentity();
1358         try {
1359             user = mWifiPermissionsWrapper.getCurrentUser();
1360         } finally {
1361             Binder.restoreCallingIdentity(ident);
1362         }
1363         return user;
1364     }
1365 
1366     /** Whether the uid is signed with the same key as the platform. */
isSignedWithPlatformKey(int uid)1367     public boolean isSignedWithPlatformKey(int uid) {
1368         return mContext.getPackageManager().checkSignatures(uid, Process.SYSTEM_UID)
1369                 == PackageManager.SIGNATURE_MATCH;
1370     }
1371 }
1372