WifiPermissionsUtil.java revision 868b692e6faa9ec3c8dd0cd42d4302082e28b992
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
17package com.android.server.wifi.util;
18
19import android.Manifest;
20import android.app.AppOpsManager;
21import android.content.Context;
22import android.content.pm.PackageManager;
23import android.content.pm.UserInfo;
24import android.os.UserManager;
25import android.provider.Settings;
26
27import com.android.server.wifi.WifiSettingsStore;
28
29import java.util.List;
30
31/**
32 * A wifi permissions utility assessing permissions
33 * for getting scan results by a package.
34 */
35public class WifiPermissionsUtil {
36    private static final String TAG = "WifiPermissionsUtil";
37    private final WifiPermissionsWrapper mWifiPermissionsWrapper;
38    private final Context mContext;
39    private final AppOpsManager mAppOps;
40    private final UserManager mUserManager;
41    private final WifiSettingsStore mSettingsStore;
42
43    public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper,
44              Context context, WifiSettingsStore settingsStore, UserManager userManager) {
45        mWifiPermissionsWrapper = wifiPermissionsWrapper;
46        mContext = context;
47        mUserManager = userManager;
48        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
49        mSettingsStore = settingsStore;
50    }
51
52    /**
53     * API to determine if the caller has permissions to get
54     * scan results.
55     * @param pkgName Packagename of the application requesting access
56     * @param uid The uid of the package
57     * @param minVersion Minimum app API Version number to enforce location permission
58     * @return boolean true or false if permissions is granted
59     */
60    public boolean canAccessScanResults(String pkgName, int uid,
61                int minVersion) throws SecurityException {
62        mAppOps.checkPackage(uid, pkgName);
63        // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS
64        // permission or is an Active Nw scorer.
65        boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid)
66                || isCallerActiveNwScorer(uid);
67        // LocationAccess by App: For AppVersion older than minVersion,
68        // it is sufficient to check if the App is foreground.
69        // Otherwise, Location Mode must be enabled and caller must have
70        // Coarse Location permission to have access to location information.
71        boolean canAppPackageUseLocation = isLegacyForeground(pkgName, minVersion)
72                || (isLocationModeEnabled(pkgName)
73                        && checkCallersLocationPermission(pkgName, uid));
74        // If neither caller or app has location access, there is no need to check
75        // any other permissions. Deny access to scan results.
76        if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
77            return false;
78        }
79        // Check if Wifi Scan request is an operation allowed for this App.
80        if (!isScanAllowedbyApps(pkgName, uid)) {
81            return false;
82        }
83        // If the User or profile is current, permission is granted
84        // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
85        if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
86            return false;
87        }
88        return true;
89    }
90
91    /**
92     * Returns true if the caller holds PEERS_MAC_ADDRESS permission.
93     */
94    private boolean checkCallerHasPeersMacAddressPermission(int uid) {
95        return mWifiPermissionsWrapper.getUidPermission(
96                android.Manifest.permission.PEERS_MAC_ADDRESS, uid)
97                == PackageManager.PERMISSION_GRANTED;
98    }
99
100    /**
101     * Returns true if the caller is an Active Network Scorer.
102     */
103    private boolean isCallerActiveNwScorer(int uid) {
104        return mWifiPermissionsWrapper.isCallerActiveNwScorer(uid);
105    }
106
107    /**
108     * Returns true if Wifi scan operation is allowed for this caller
109     * and package.
110     */
111    private boolean isScanAllowedbyApps(String pkgName, int uid) {
112        return checkAppOpAllowed(AppOpsManager.OP_WIFI_SCAN, pkgName, uid);
113    }
114
115    /**
116     * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
117     */
118    private boolean checkInteractAcrossUsersFull(int uid) {
119        return mWifiPermissionsWrapper.getUidPermission(
120                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
121                == PackageManager.PERMISSION_GRANTED;
122    }
123
124    /**
125     * Returns true if the calling user is the current one or a profile of the
126     * current user.
127     */
128    private boolean isCurrentProfile(int uid) {
129        int currentUser = mWifiPermissionsWrapper.getCurrentUser();
130        int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid);
131        if (callingUserId == currentUser) {
132            return true;
133        } else {
134            List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser);
135            for (UserInfo user: userProfiles) {
136                if (user.id == callingUserId) {
137                    return true;
138                }
139            }
140        }
141        return false;
142    }
143
144    /**
145     * Returns true if the App version is older than minVersion.
146     */
147    private boolean isLegacyVersion(String pkgName, int minVersion) {
148        try {
149            if (mContext.getPackageManager().getApplicationInfo(pkgName, 0)
150                    .targetSdkVersion < minVersion) {
151                return true;
152            }
153        } catch (PackageManager.NameNotFoundException e) {
154            // In case of exception, assume known app (more strict checking)
155            // Note: This case will never happen since checkPackage is
156            // called to verify valididity before checking App's version.
157        }
158        return false;
159    }
160
161    private boolean checkAppOpAllowed(int op, String pkgName, int uid) {
162        return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED;
163    }
164
165    private boolean isLegacyForeground(String pkgName, int version) {
166        return isLegacyVersion(pkgName, version) && isForegroundApp(pkgName);
167    }
168
169    private boolean isForegroundApp(String pkgName) {
170        return pkgName.equals(mWifiPermissionsWrapper.getTopPkgName());
171    }
172
173    /**
174     * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION
175     * and a corresponding app op is allowed for this package and uid.
176     */
177    private boolean checkCallersLocationPermission(String pkgName, int uid) {
178        // Coarse Permission implies Fine permission
179        if ((mWifiPermissionsWrapper.getUidPermission(
180                Manifest.permission.ACCESS_COARSE_LOCATION, uid)
181                == PackageManager.PERMISSION_GRANTED)
182                && checkAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid)) {
183            return true;
184        }
185        return false;
186    }
187    private boolean isLocationModeEnabled(String pkgName) {
188        // Location mode check on applications that are later than version.
189        return (mSettingsStore.getLocationModeSetting(mContext)
190                 != Settings.Secure.LOCATION_MODE_OFF);
191    }
192}
193