• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.phone;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.UserIdInt;
22 import android.app.ActivityManager;
23 import android.app.AppOpsManager;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.content.pm.UserInfo;
27 import android.os.Build;
28 import android.os.UserHandle;
29 import android.os.UserManager;
30 import android.provider.Settings;
31 
32 import java.util.List;
33 
34 /**
35  * Helper for performing location access checks.
36  */
37 final class LocationAccessPolicy {
38 
LocationAccessPolicy()39     private LocationAccessPolicy() {
40         /* do nothing - hide ctor */
41     }
42 
43     /**
44      * API to determine if the caller has permissions to get cell location.
45      *
46      * @param pkgName Package name of the application requesting access
47      * @param uid The uid of the package
48      * @param message Message to add to the exception if no location permission
49      * @return boolean true or false if permissions is granted
50      */
canAccessCellLocation(@onNull Context context, @NonNull String pkgName, int uid, String message)51     static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
52             int uid, String message) throws SecurityException {
53         context.getSystemService(AppOpsManager.class).checkPackage(uid, pkgName);
54         // We always require the location permission and also require the
55         // location mode to be on for non-legacy apps. Legacy apps are
56         // required to be in the foreground to at least mitigate the case
57         // where a legacy app the user is not using tracks their location.
58 
59         // Grating ACCESS_FINE_LOCATION to an app automatically grants it ACCESS_COARSE_LOCATION.
60         context.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION, message);
61         final int opCode = AppOpsManager.permissionToOpCode(
62                 Manifest.permission.ACCESS_COARSE_LOCATION);
63         if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
64                 .noteOp(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
65             return false;
66         }
67         if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
68                 && !isLegacyForeground(context, pkgName)) {
69             return false;
70         }
71         // If the user or profile is current, permission is granted.
72         // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
73         return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
74     }
75 
isLocationModeEnabled(@onNull Context context, @UserIdInt int userId)76     private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
77         return Settings.Secure.getIntForUser(context.getContentResolver(),
78                 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId)
79                 != Settings.Secure.LOCATION_MODE_OFF;
80     }
81 
isLegacyForeground(@onNull Context context, @NonNull String pkgName)82     private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName) {
83         return isLegacyVersion(context, pkgName) && isForegroundApp(context, pkgName);
84     }
85 
isLegacyVersion(@onNull Context context, @NonNull String pkgName)86     private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
87         try {
88             if (context.getPackageManager().getApplicationInfo(pkgName, 0)
89                     .targetSdkVersion <= Build.VERSION_CODES.O) {
90                 return true;
91             }
92         } catch (PackageManager.NameNotFoundException e) {
93             // In case of exception, assume known app (more strict checking)
94             // Note: This case will never happen since checkPackage is
95             // called to verify validity before checking app's version.
96         }
97         return false;
98     }
99 
isForegroundApp(@onNull Context context, @NonNull String pkgName)100     private static boolean isForegroundApp(@NonNull Context context, @NonNull String pkgName) {
101         final ActivityManager am = context.getSystemService(ActivityManager.class);
102         final List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
103         if (!tasks.isEmpty()) {
104             return pkgName.equals(tasks.get(0).topActivity.getPackageName());
105         }
106         return false;
107     }
108 
checkInteractAcrossUsersFull(@onNull Context context)109     private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
110         return context.checkCallingOrSelfPermission(
111                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
112                 == PackageManager.PERMISSION_GRANTED;
113     }
114 
isCurrentProfile(@onNull Context context, int uid)115     private static boolean isCurrentProfile(@NonNull Context context, int uid) {
116         final int currentUser = ActivityManager.getCurrentUser();
117         final int callingUserId = UserHandle.getUserId(uid);
118         if (callingUserId == currentUser) {
119             return true;
120         } else {
121             List<UserInfo> userProfiles = context.getSystemService(
122                     UserManager.class).getProfiles(currentUser);
123             for (UserInfo user: userProfiles) {
124                 if (user.id == callingUserId) {
125                     return true;
126                 }
127             }
128         }
129         return false;
130     }
131 }
132