1 /* 2 * Copyright 2021 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.net.module.util; 18 19 import static android.Manifest.permission.ACCESS_NETWORK_STATE; 20 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; 21 import static android.Manifest.permission.NETWORK_STACK; 22 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; 23 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 24 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.content.Context; 29 import android.content.pm.PackageInfo; 30 import android.os.Binder; 31 32 import java.io.PrintWriter; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.List; 37 38 /** 39 * Collection of permission utilities. 40 * @hide 41 */ 42 public final class PermissionUtils { 43 /** 44 * Return true if the context has one of given permission. 45 */ checkAnyPermissionOf(@onNull Context context, @NonNull String... permissions)46 public static boolean checkAnyPermissionOf(@NonNull Context context, 47 @NonNull String... permissions) { 48 for (String permission : permissions) { 49 if (context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { 50 return true; 51 } 52 } 53 return false; 54 } 55 56 /** 57 * Return true if the context has one of give permission that is allowed 58 * for a particular process and user ID running in the system. 59 */ checkAnyPermissionOf(@onNull Context context, int pid, int uid, @NonNull String... permissions)60 public static boolean checkAnyPermissionOf(@NonNull Context context, 61 int pid, int uid, @NonNull String... permissions) { 62 for (String permission : permissions) { 63 if (context.checkPermission(permission, pid, uid) == PERMISSION_GRANTED) { 64 return true; 65 } 66 } 67 return false; 68 } 69 70 /** 71 * Enforce permission check on the context that should have one of given permission. 72 */ enforceAnyPermissionOf(@onNull Context context, @NonNull String... permissions)73 public static void enforceAnyPermissionOf(@NonNull Context context, 74 @NonNull String... permissions) { 75 if (!checkAnyPermissionOf(context, permissions)) { 76 throw new SecurityException("Requires one of the following permissions: " 77 + String.join(", ", permissions) + "."); 78 } 79 } 80 81 /** 82 * If the NetworkStack, MAINLINE_NETWORK_STACK are not allowed for a particular process, throw a 83 * {@link SecurityException}. 84 * 85 * @param context {@link android.content.Context} for the process. 86 */ enforceNetworkStackPermission(final @NonNull Context context)87 public static void enforceNetworkStackPermission(final @NonNull Context context) { 88 enforceNetworkStackPermissionOr(context); 89 } 90 91 /** 92 * If the NetworkStack, MAINLINE_NETWORK_STACK or other specified permissions are not allowed 93 * for a particular process, throw a {@link SecurityException}. 94 * 95 * @param context {@link android.content.Context} for the process. 96 * @param otherPermissions The set of permissions that could be the candidate permissions , or 97 * empty string if none of other permissions needed. 98 */ enforceNetworkStackPermissionOr(final @NonNull Context context, final @NonNull String... otherPermissions)99 public static void enforceNetworkStackPermissionOr(final @NonNull Context context, 100 final @NonNull String... otherPermissions) { 101 ArrayList<String> permissions = new ArrayList<String>(Arrays.asList(otherPermissions)); 102 permissions.add(NETWORK_STACK); 103 permissions.add(PERMISSION_MAINLINE_NETWORK_STACK); 104 enforceAnyPermissionOf(context, permissions.toArray(new String[0])); 105 } 106 107 /** 108 * If the CONNECTIVITY_USE_RESTRICTED_NETWORKS is not allowed for a particular process, throw a 109 * {@link SecurityException}. 110 * 111 * @param context {@link android.content.Context} for the process. 112 * @param message A message to include in the exception if it is thrown. 113 */ enforceRestrictedNetworkPermission( final @NonNull Context context, final @Nullable String message)114 public static void enforceRestrictedNetworkPermission( 115 final @NonNull Context context, final @Nullable String message) { 116 context.enforceCallingOrSelfPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, message); 117 } 118 119 /** 120 * If the ACCESS_NETWORK_STATE is not allowed for a particular process, throw a 121 * {@link SecurityException}. 122 * 123 * @param context {@link android.content.Context} for the process. 124 * @param message A message to include in the exception if it is thrown. 125 */ enforceAccessNetworkStatePermission( final @NonNull Context context, final @Nullable String message)126 public static void enforceAccessNetworkStatePermission( 127 final @NonNull Context context, final @Nullable String message) { 128 context.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, message); 129 } 130 131 /** 132 * Return true if the context has DUMP permission. 133 */ checkDumpPermission(Context context, String tag, PrintWriter pw)134 public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) { 135 if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 136 != PERMISSION_GRANTED) { 137 pw.println("Permission Denial: can't dump " + tag + " from from pid=" 138 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 139 + " due to missing android.permission.DUMP permission"); 140 return false; 141 } else { 142 return true; 143 } 144 } 145 146 /** 147 * Enforce that a given feature is available and if not, throw an 148 * {@link UnsupportedOperationException}. 149 * 150 * @param context {@link android.content.Context} for the process. 151 * @param feature the feature name to enforce. 152 * @param errorMessage an optional error message to include. 153 */ enforceSystemFeature(final @NonNull Context context, final @NonNull String feature, final @Nullable String errorMessage)154 public static void enforceSystemFeature(final @NonNull Context context, 155 final @NonNull String feature, final @Nullable String errorMessage) { 156 final boolean hasSystemFeature = 157 context.getPackageManager().hasSystemFeature(feature); 158 if (!hasSystemFeature) { 159 if (null == errorMessage) { 160 throw new UnsupportedOperationException(); 161 } 162 throw new UnsupportedOperationException(errorMessage); 163 } 164 } 165 166 /** 167 * Get the list of granted permissions for a package info. 168 * 169 * PackageInfo contains the list of requested permissions, and their state (whether they 170 * were granted or not, in particular) as a parallel array. Most users care only about 171 * granted permissions. This method returns the list of them. 172 * 173 * @param packageInfo the package info for the relevant uid. 174 * @return the list of granted permissions. 175 */ getGrantedPermissions(final @NonNull PackageInfo packageInfo)176 public static List<String> getGrantedPermissions(final @NonNull PackageInfo packageInfo) { 177 if (null == packageInfo.requestedPermissions) return Collections.emptyList(); 178 final ArrayList<String> result = new ArrayList<>(packageInfo.requestedPermissions.length); 179 for (int i = 0; i < packageInfo.requestedPermissions.length; ++i) { 180 if (0 != (REQUESTED_PERMISSION_GRANTED & packageInfo.requestedPermissionsFlags[i])) { 181 result.add(packageInfo.requestedPermissions[i]); 182 } 183 } 184 return result; 185 } 186 } 187