1 /*
2  * Copyright (C) 2020 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.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
20 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.app.ActivityManager;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageManager;
27 import android.content.res.Resources;
28 import android.os.Process;
29 import android.os.UserHandle;
30 import android.os.WorkSource;
31 import android.text.TextUtils;
32 import android.util.Log;
33 
34 import com.android.wifi.resources.R;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.Arrays;
39 
40 /**
41  * Class for wrapping a WorkSource object and providing some (wifi specific) utility methods.
42  *
43  * This is primarily used in {@link com.android.server.wifi.HalDeviceManager} class.
44  */
45 public class WorkSourceHelper {
46     private static final String TAG = "WorkSourceHelper";
47     private static final int APP_INFO_FLAGS_SYSTEM_APP =
48             ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
49     private final WorkSource mWorkSource;
50     private final WifiPermissionsUtil mWifiPermissionsUtil;
51     private final ActivityManager mActivityManager;
52     private final PackageManager mPackageManager;
53     private final Resources mResources;
54 
55     // Internal opportunistic request.
56     public static final int PRIORITY_INTERNAL = 0;
57 
58     // Request from a background app.
59     public static final int PRIORITY_BG = 1;
60 
61     // Request from a foreground service.
62     public static final int PRIORITY_FG_SERVICE = 2;
63 
64     // Request from a foreground app.
65     public static final int PRIORITY_FG_APP = 3;
66 
67     // Request from a system app.
68     public static final int PRIORITY_SYSTEM = 4;
69 
70     // Request from an app with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD or NETWORK_STACK permission.
71     public static final int PRIORITY_PRIVILEGED = 5;
72 
73     // Keep these in sync with any additions/deletions to above buckets.
74     public static final int PRIORITY_MIN = PRIORITY_INTERNAL;
75     public static final int PRIORITY_MAX = PRIORITY_PRIVILEGED;
76     @IntDef(prefix = { "PRIORITY_" }, value = {
77             PRIORITY_INTERNAL,
78             PRIORITY_BG,
79             PRIORITY_FG_SERVICE,
80             PRIORITY_FG_APP,
81             PRIORITY_SYSTEM,
82             PRIORITY_PRIVILEGED,
83     })
84     @Retention(RetentionPolicy.SOURCE)
85     public @interface RequestorWsPriority {}
86 
87     /**
88      * Returns integer priority level for the provided |ws|.
89      */
getRequestorWsPriority()90     public @RequestorWsPriority int getRequestorWsPriority() {
91         @RequestorWsPriority int totalPriority = PRIORITY_INTERNAL;
92         for (int i = 0; i < mWorkSource.size(); i++) {
93             String packageName = mWorkSource.getPackageName(i);
94             int uid = mWorkSource.getUid(i);
95             final @RequestorWsPriority int priority;
96             if (uid == Process.WIFI_UID) {
97                 priority = PRIORITY_INTERNAL;
98             } else if (isPrivileged(uid)) {
99                 priority = PRIORITY_PRIVILEGED;
100             } else if (isSystem(packageName, uid)) {
101                 priority = PRIORITY_SYSTEM;
102             } else if (isForegroundApp(packageName)) {
103                 priority = PRIORITY_FG_APP;
104             } else if (isForegroundService(packageName)) {
105                 priority = PRIORITY_FG_SERVICE;
106             } else {
107                 priority = PRIORITY_BG;
108             }
109             if (priority > totalPriority) {
110                 totalPriority = priority;
111             }
112         }
113         return totalPriority;
114     }
115 
WorkSourceHelper( @onNull WorkSource workSource, @NonNull WifiPermissionsUtil wifiPermissionsUtil, @NonNull ActivityManager activityManager, @NonNull PackageManager packageManager, @NonNull Resources resources)116     public WorkSourceHelper(
117             @NonNull WorkSource workSource,
118             @NonNull WifiPermissionsUtil wifiPermissionsUtil,
119             @NonNull ActivityManager activityManager,
120             @NonNull PackageManager packageManager,
121             @NonNull Resources resources) {
122         mWorkSource = workSource;
123         mWifiPermissionsUtil = wifiPermissionsUtil;
124         mActivityManager = activityManager;
125         mPackageManager = packageManager;
126         mResources = resources;
127     }
128 
getWorkSource()129     public WorkSource getWorkSource() {
130         return mWorkSource;
131     }
132 
133     @Override
toString()134     public String toString() {
135         return mWorkSource.toString();
136     }
137 
138     /**
139      * Check if the request comes from an app with privileged permissions.
140      */
isPrivileged(int uid)141     private boolean isPrivileged(int uid) {
142         return mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
143                 || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)
144                 || mWifiPermissionsUtil.checkNetworkStackPermission(uid)
145                 || mWifiPermissionsUtil.checkMainlineNetworkStackPermission(uid);
146     }
147 
148     /**
149      * Check if the request comes from a system app.
150      */
isSystem(String packageName, int uid)151     private boolean isSystem(String packageName, int uid) {
152         // when checking ActiveModeWarden#INTERNAL_REQUESTOR_WS
153         if (packageName == null) {
154             return false;
155         }
156         try {
157             ApplicationInfo info = mPackageManager.getApplicationInfoAsUser(
158                     packageName, 0, UserHandle.getUserHandleForUid(uid));
159             return (info.flags & APP_INFO_FLAGS_SYSTEM_APP) != 0;
160         } catch (PackageManager.NameNotFoundException e) {
161             Log.e(TAG, "Failed to retrieve app info for packageName=" + packageName + " uid=" + uid,
162                     e);
163             // In case of exception, assume unknown app (more strict checking)
164             // Note: This case will never happen since checkPackage is
165             // called to verify validity before checking App's version.
166             return false;
167         }
168     }
169 
170     /**
171      * Check if the request comes from a foreground app.
172      */
isForegroundApp(@onNull String requestorPackageName)173     private boolean isForegroundApp(@NonNull String requestorPackageName) {
174         String[] exceptionList = mResources.getStringArray(
175                 R.array.config_wifiInterfacePriorityTreatAsForegroundList);
176         if (exceptionList != null && Arrays.stream(exceptionList).anyMatch(
177                 s -> TextUtils.equals(requestorPackageName, s))) {
178             return true;
179         }
180         try {
181             return mActivityManager.getPackageImportance(requestorPackageName)
182                     <= IMPORTANCE_FOREGROUND;
183         } catch (SecurityException e) {
184             Log.e(TAG, "Failed to check the app state", e);
185             return false;
186         }
187     }
188 
189     /**
190      * Check if the request comes from a foreground service.
191      */
isForegroundService(@onNull String requestorPackageName)192     private boolean isForegroundService(@NonNull String requestorPackageName) {
193         try {
194             int importance = mActivityManager.getPackageImportance(requestorPackageName);
195             return IMPORTANCE_FOREGROUND < importance
196                     && importance <= IMPORTANCE_FOREGROUND_SERVICE;
197         } catch (SecurityException e) {
198             Log.e(TAG, "Failed to check the app state", e);
199             return false;
200         }
201     }
202 }
203