1 /* 2 * Copyright 2020, Google LLC 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * Neither the name of Google LLC nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 package com.android.tools.smali.dexlib2; 32 33 import com.google.common.collect.ImmutableSet; 34 import com.google.common.collect.ImmutableSet.Builder; 35 36 import java.util.HashMap; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.StringJoiner; 40 41 public enum HiddenApiRestriction { 42 WHITELIST(0, "whitelist", false), 43 GREYLIST(1, "greylist", false), 44 BLACKLIST(2, "blacklist", false), 45 GREYLIST_MAX_O(3, "greylist-max-o", false), 46 GREYLIST_MAX_P(4, "greylist-max-p", false), 47 GREYLIST_MAX_Q(5, "greylist-max-q", false), 48 GREYLIST_MAX_R(6, "greylist-max-r", false), 49 CORE_PLATFORM_API(8, "core-platform-api", true), 50 TEST_API(16, "test-api", true); 51 52 private static final HiddenApiRestriction[] hiddenApiFlags = new HiddenApiRestriction[] { 53 WHITELIST, 54 GREYLIST, 55 BLACKLIST, 56 GREYLIST_MAX_O, 57 GREYLIST_MAX_P, 58 GREYLIST_MAX_Q, 59 GREYLIST_MAX_R 60 }; 61 62 private static final HiddenApiRestriction[] domainSpecificApiFlags = new HiddenApiRestriction[] { 63 CORE_PLATFORM_API, 64 TEST_API 65 }; 66 67 private static final Map<String, HiddenApiRestriction> hiddenApiRestrictionsByName; 68 69 static { 70 hiddenApiRestrictionsByName = new HashMap<>(); 71 for (HiddenApiRestriction hiddenApiRestriction : HiddenApiRestriction.values()) { hiddenApiRestriction.toString()72 hiddenApiRestrictionsByName.put(hiddenApiRestriction.toString(), hiddenApiRestriction); 73 } 74 } 75 76 private static final int HIDDENAPI_FLAG_MASK = 0x7; 77 78 private final int value; 79 private final String name; 80 private final boolean isDomainSpecificApiFlag; 81 HiddenApiRestriction(int value, String name, boolean isDomainSpecificApiFlag)82 HiddenApiRestriction(int value, String name, boolean isDomainSpecificApiFlag) { 83 this.value = value; 84 this.name = name; 85 this.isDomainSpecificApiFlag = isDomainSpecificApiFlag; 86 } 87 toString()88 public String toString() { 89 return name; 90 } 91 getValue()92 public int getValue() { 93 return value; 94 } 95 isSet(int value)96 public boolean isSet(int value) { 97 if (isDomainSpecificApiFlag) { 98 return (value & this.value) != 0; 99 } else { 100 return (value & HIDDENAPI_FLAG_MASK) == this.value; 101 } 102 } 103 isDomainSpecificApiFlag()104 public boolean isDomainSpecificApiFlag() { 105 return isDomainSpecificApiFlag; 106 } 107 getAllFlags(int value)108 public static Set<HiddenApiRestriction> getAllFlags(int value) { 109 HiddenApiRestriction normalRestriction = hiddenApiFlags[value & HIDDENAPI_FLAG_MASK]; 110 111 int domainSpecificPart = (value & ~HIDDENAPI_FLAG_MASK); 112 if (domainSpecificPart == 0) { 113 return ImmutableSet.of(normalRestriction); 114 } 115 Builder<HiddenApiRestriction> builder = ImmutableSet.builder(); 116 builder.add(normalRestriction); 117 for (HiddenApiRestriction domainSpecificApiFlag : domainSpecificApiFlags) { 118 if (domainSpecificApiFlag.isSet(value)) { 119 builder.add(domainSpecificApiFlag); 120 } 121 } 122 return builder.build(); 123 } 124 formatHiddenRestrictions(int value)125 public static String formatHiddenRestrictions(int value) { 126 StringJoiner joiner = new StringJoiner("|"); 127 for (HiddenApiRestriction hiddenApiRestriction : getAllFlags(value)) { 128 joiner.add(hiddenApiRestriction.toString()); 129 } 130 return joiner.toString(); 131 } 132 combineFlags(Iterable<HiddenApiRestriction> flags)133 public static int combineFlags(Iterable<HiddenApiRestriction> flags) { 134 boolean gotHiddenApiFlag = false; 135 136 int value = 0; 137 138 for (HiddenApiRestriction flag : flags) { 139 if (flag.isDomainSpecificApiFlag) { 140 value += flag.value; 141 } else { 142 if (gotHiddenApiFlag) { 143 throw new IllegalArgumentException( 144 "Cannot combine multiple flags for hidden api restrictions"); 145 } 146 gotHiddenApiFlag = true; 147 value += flag.value; 148 } 149 } 150 151 return value; 152 } 153 forName(String name)154 public static HiddenApiRestriction forName(String name) { 155 return hiddenApiRestrictionsByName.get(name); 156 } 157 } 158