1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.KITKAT; 4 5 import android.app.AppOpsManager; 6 import android.app.AppOpsManager.OpEntry; 7 import android.app.AppOpsManager.PackageOps; 8 import android.os.Build; 9 import com.google.common.collect.HashMultimap; 10 import com.google.common.collect.ImmutableList; 11 import com.google.common.collect.Multimap; 12 import java.util.ArrayList; 13 import java.util.HashSet; 14 import java.util.List; 15 import java.util.Set; 16 import org.robolectric.RuntimeEnvironment; 17 import org.robolectric.annotation.HiddenApi; 18 import org.robolectric.annotation.Implementation; 19 import org.robolectric.annotation.Implements; 20 import org.robolectric.util.ReflectionHelpers; 21 import org.robolectric.util.ReflectionHelpers.ClassParameter; 22 23 /** 24 * Shadow for the {@link AppOpsManager}. 25 */ 26 @Implements(value = AppOpsManager.class) 27 public class ShadowAppOpsManager { 28 29 // OpEntry fields that the shadow doesn't currently allow the test to configure. 30 private static final long OP_TIME = 1400000000L; 31 private static final long REJECT_TIME = 0L; 32 private static final int DURATION = 10; 33 private static final int PROXY_UID = 0; 34 private static final String PROXY_PACKAGE = ""; 35 36 // Recorded operations, keyed by "uid|packageName" 37 Multimap<String, Integer> mStoredOps = HashMultimap.create(); 38 39 @Implementation(minSdk = KITKAT) 40 public int noteOp(int op, int uid, String packageName) { 41 mStoredOps.put(getInternalKey(uid, packageName), op); 42 43 // Permission check not currently implemented in this shadow. 44 return AppOpsManager.MODE_ALLOWED; 45 } 46 47 @Implementation(minSdk = KITKAT) 48 @HiddenApi 49 public List<PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) { 50 Set<Integer> opFilter = new HashSet<>(); 51 if (ops != null) { 52 for (int op : ops) { 53 opFilter.add(op); 54 } 55 } 56 57 List<OpEntry> opEntries = new ArrayList<>(); 58 for (Integer op : mStoredOps.get(getInternalKey(uid, packageName))) { 59 if (opFilter.isEmpty() || opFilter.contains(op)) { 60 opEntries.add(toOpEntry(op)); 61 } 62 } 63 64 return ImmutableList.of(new PackageOps(packageName, uid, opEntries)); 65 } 66 67 private static OpEntry toOpEntry(Integer op) { 68 if (RuntimeEnvironment.getApiLevel() < Build.VERSION_CODES.M) { 69 return (OpEntry) ReflectionHelpers.callConstructor( 70 OpEntry.class, 71 ClassParameter.from(int.class, op), 72 ClassParameter.from(int.class, AppOpsManager.MODE_ALLOWED), 73 ClassParameter.from(long.class, OP_TIME), 74 ClassParameter.from(long.class, REJECT_TIME), 75 ClassParameter.from(int.class, DURATION)); 76 } 77 78 return new OpEntry( 79 op, AppOpsManager.MODE_ALLOWED, OP_TIME, REJECT_TIME, DURATION, PROXY_UID, PROXY_PACKAGE); 80 } 81 82 private static String getInternalKey(int uid, String packageName) { 83 return uid + "|" + packageName; 84 } 85 } 86