1 /* 2 * Copyright (C) 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.bedstead.permissions; 18 19 import android.util.Log; 20 21 import com.android.bedstead.nene.TestApis; 22 import com.android.bedstead.nene.exceptions.NeneException; 23 import com.android.bedstead.nene.utils.Versions; 24 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.HashSet; 28 import java.util.List; 29 import java.util.Set; 30 import java.util.concurrent.TimeUnit; 31 import java.util.concurrent.locks.Lock; 32 import java.util.concurrent.locks.ReentrantLock; 33 34 /** 35 * Default implementation of {@link PermissionContext} 36 */ 37 public final class PermissionContextImpl implements PermissionContextModifier { 38 39 private final Permissions mPermissions; 40 private final Set<String> mGrantedPermissions = new HashSet<>(); 41 private final Set<String> mDeniedPermissions = new HashSet<>(); 42 private final Set<String> mGrantedAppOps = new HashSet<>(); 43 private final Set<String> mDeniedAppOps = new HashSet<>(); 44 private final boolean mNonBlockingContext; 45 46 private static final Lock sPermissionsLock = new ReentrantLock(); 47 private static final String TAG = "PermissionContextImpl"; 48 private static final List<PermissionContextImpl> sNotClosedContexts = new ArrayList<>(); 49 50 /** 51 * creates PermissionContextImpl instance 52 * it won't be executed until other threads will stop using permissions contexts 53 */ create(Permissions permissions)54 static PermissionContextImpl create(Permissions permissions) { 55 Log.v(TAG, "locking permissionsLock..."); 56 try { 57 boolean success = sPermissionsLock.tryLock(90, TimeUnit.SECONDS); 58 if (!success) { 59 var exception = new NeneException("unable to obtain the lock in " 60 + "PermissionContextImpl, make sure to not use permission contexts from " 61 + "more than one thread at a time"); 62 Log.e(TAG, Log.getStackTraceString(exception)); 63 throw exception; 64 } 65 } catch (InterruptedException e) { 66 Log.w(TAG, "unable to obtain the lock in PermissionContextImpl", e); 67 } 68 Log.v(TAG, "permissionsLock locked"); 69 return new PermissionContextImpl(permissions, false); 70 } 71 72 /** 73 * creates a special version of PermissionContextImpl that allows other threads to create new 74 * PermissionContextImpl objects, it's designed to be used only for handling annotations 75 */ createNonBlocking(Permissions permissions)76 static PermissionContextImpl createNonBlocking(Permissions permissions) { 77 Log.v(TAG, "creating non blocking context"); 78 return new PermissionContextImpl(permissions, true); 79 } 80 closeAllContexts()81 static void closeAllContexts() { 82 for (int i = 0; i < sNotClosedContexts.size(); i++) { 83 PermissionContextImpl notClosedContext = sNotClosedContexts.get(i); 84 Log.w(TAG, "closing not closed context: " 85 + (i + 1) + " of " + sNotClosedContexts.size() 86 + ", it should have been closed by now"); 87 notClosedContext.close(); 88 } 89 } 90 PermissionContextImpl(Permissions permissions, boolean nonBlockingContext)91 private PermissionContextImpl(Permissions permissions, boolean nonBlockingContext) { 92 mPermissions = permissions; 93 mNonBlockingContext = nonBlockingContext; 94 sNotClosedContexts.add(this); 95 } 96 grantedPermissions()97 Set<String> grantedPermissions() { 98 return mGrantedPermissions; 99 } 100 deniedPermissions()101 Set<String> deniedPermissions() { 102 return mDeniedPermissions; 103 } 104 grantedAppOps()105 Set<String> grantedAppOps() { 106 return mGrantedAppOps; 107 } 108 deniedAppOps()109 Set<String> deniedAppOps() { 110 return mDeniedAppOps; 111 } 112 113 /** 114 * See {@link Permissions#withPermission(String...)} 115 */ 116 @Override withPermission(String... permissions)117 public PermissionContextImpl withPermission(String... permissions) { 118 for (String permission : permissions) { 119 if (mDeniedPermissions.contains(permission)) { 120 mPermissions.clearPermissions(); 121 close(); 122 throw new NeneException( 123 permission + " cannot be required to be both granted and denied"); 124 } 125 } 126 127 mGrantedPermissions.addAll(Arrays.asList(permissions)); 128 129 applyPermissions(); 130 131 return this; 132 } 133 134 /** 135 * See {@link Permissions#withPermissionOnVersion(int, String...)} 136 */ 137 @Override withPermissionOnVersion(int sdkVersion, String... permissions)138 public PermissionContextImpl withPermissionOnVersion(int sdkVersion, String... permissions) { 139 return withPermissionOnVersionBetween(sdkVersion, sdkVersion, permissions); 140 } 141 142 /** 143 * See {@link Permissions#withPermissionOnVersionAtLeast(int, String...)} 144 */ 145 @Override withPermissionOnVersionAtLeast( int sdkVersion, String... permissions)146 public PermissionContextImpl withPermissionOnVersionAtLeast( 147 int sdkVersion, String... permissions) { 148 return withPermissionOnVersionBetween(sdkVersion, Versions.ANY, permissions); 149 } 150 151 /** 152 * See {@link Permissions#withPermissionOnVersionAtMost(int, String...)} 153 */ 154 @Override withPermissionOnVersionAtMost( int sdkVersion, String... permissions)155 public PermissionContextImpl withPermissionOnVersionAtMost( 156 int sdkVersion, String... permissions) { 157 return withPermissionOnVersionBetween(Versions.ANY, sdkVersion, permissions); 158 } 159 160 /** 161 * See {@link Permissions#withPermissionOnVersionBetween(int, String...)} 162 */ 163 @Override withPermissionOnVersionBetween( int minSdkVersion, int maxSdkVersion, String... permissions)164 public PermissionContextImpl withPermissionOnVersionBetween( 165 int minSdkVersion, int maxSdkVersion, String... permissions) { 166 if (Versions.meetsSdkVersionRequirements(minSdkVersion, maxSdkVersion)) { 167 return withPermission(permissions); 168 } 169 170 return this; 171 } 172 173 /** 174 * See {@link Permissions#withoutPermission(String...)} 175 */ 176 @Override withoutPermission(String... permissions)177 public PermissionContextImpl withoutPermission(String... permissions) { 178 for (String permission : permissions) { 179 if (mGrantedPermissions.contains(permission)) { 180 mPermissions.clearPermissions(); 181 close(); 182 throw new NeneException( 183 permission + " cannot be required to be both granted and denied"); 184 } 185 } 186 187 if (TestApis.packages().instrumented().isInstantApp()) { 188 close(); 189 throw new NeneException( 190 "Tests which use withoutPermission must not run as instant apps. If you are" 191 + "using DeviceState, use the @RequireNotInstantApp annotation."); 192 } 193 194 mDeniedPermissions.addAll(Arrays.asList(permissions)); 195 196 applyPermissions(); 197 198 return this; 199 } 200 201 /** 202 * See {@link Permissions#withAppOp(String...)} 203 */ 204 @Override withAppOp(String... appOps)205 public PermissionContextImpl withAppOp(String... appOps) { 206 for (String appOp : appOps) { 207 if (mDeniedAppOps.contains(appOp)) { 208 mPermissions.clearPermissions(); 209 close(); 210 throw new NeneException( 211 appOp + " cannot be required to be both granted and denied"); 212 } 213 } 214 215 mGrantedAppOps.addAll(Arrays.asList(appOps)); 216 217 applyPermissions(); 218 219 return this; 220 } 221 222 /** 223 * See {@link Permissions#withAppOpOnVersion(int, String...)} 224 */ 225 @Override withAppOpOnVersion(int sdkVersion, String... appOps)226 public PermissionContextImpl withAppOpOnVersion(int sdkVersion, String... appOps) { 227 return withAppOpOnVersionBetween(sdkVersion, sdkVersion, appOps); 228 } 229 230 /** 231 * See {@link Permissions#withAppOpOnVersionAtMost(int, String...)} 232 */ 233 @Override withAppOpOnVersionAtMost(int sdkVersion, String... appOps)234 public PermissionContextImpl withAppOpOnVersionAtMost(int sdkVersion, String... appOps) { 235 return withAppOpOnVersionBetween(Versions.ANY, sdkVersion, appOps); 236 } 237 238 /** 239 * See {@link Permissions#withAppOpOnVersionAtLeast(int, String...)} 240 */ 241 @Override withAppOpOnVersionAtLeast(int sdkVersion, String... appOps)242 public PermissionContextImpl withAppOpOnVersionAtLeast(int sdkVersion, String... appOps) { 243 return withAppOpOnVersionBetween(sdkVersion, Versions.ANY, appOps); 244 } 245 246 /** 247 * See {@link Permissions#withAppOpOnVersionBetween(int, String...)} 248 */ 249 @Override withAppOpOnVersionBetween( int minSdkVersion, int maxSdkVersion, String... appOps)250 public PermissionContextImpl withAppOpOnVersionBetween( 251 int minSdkVersion, int maxSdkVersion, String... appOps) { 252 if (Versions.meetsSdkVersionRequirements(minSdkVersion, maxSdkVersion)) { 253 return withAppOp(appOps); 254 } 255 256 return this; 257 } 258 259 /** 260 * See {@link Permissions#withoutAppOp(String...)}. 261 */ 262 @Override withoutAppOp(String... appOps)263 public PermissionContextImpl withoutAppOp(String... appOps) { 264 for (String appOp : appOps) { 265 if (mGrantedAppOps.contains(appOp)) { 266 mPermissions.clearPermissions(); 267 close(); 268 throw new NeneException( 269 appOp + " cannot be required to be both granted and denied"); 270 } 271 } 272 273 mDeniedAppOps.addAll(Arrays.asList(appOps)); 274 275 applyPermissions(); 276 277 return this; 278 } 279 applyPermissions()280 private void applyPermissions() { 281 try { 282 mPermissions.applyPermissions(); 283 } catch (NeneException e) { 284 Log.d(TAG, "caught NeneException while executing applyPermissions(), " 285 + "closing the context"); 286 close(); 287 throw e; 288 } 289 } 290 291 @Override close()292 public void close() { 293 Permissions.sInstance.undoPermission(this); 294 if (!mNonBlockingContext) { 295 sPermissionsLock.unlock(); 296 Log.v(TAG, "permissionsLock unlocked"); 297 } 298 sNotClosedContexts.remove(this); 299 } 300 } 301