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