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 android.location.util.identity; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.os.Binder; 22 import android.os.Process; 23 import android.os.UserHandle; 24 import android.os.WorkSource; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.internal.util.ArrayUtils; 28 import com.android.internal.util.HexDump; 29 30 import java.util.Objects; 31 32 /** 33 * Identifying information on a caller. 34 * 35 * @hide 36 */ 37 public final class CallerIdentity { 38 39 /** 40 * Construct a CallerIdentity for test purposes. 41 */ 42 @VisibleForTesting forTest(int uid, int pid, String packageName, @Nullable String attributionTag)43 public static CallerIdentity forTest(int uid, int pid, String packageName, 44 @Nullable String attributionTag) { 45 return forTest(uid, pid, packageName, attributionTag, null); 46 } 47 48 /** 49 * Construct a CallerIdentity for test purposes. 50 */ 51 @VisibleForTesting forTest(int uid, int pid, String packageName, @Nullable String attributionTag, @Nullable String listenerId)52 public static CallerIdentity forTest(int uid, int pid, String packageName, 53 @Nullable String attributionTag, @Nullable String listenerId) { 54 return new CallerIdentity(uid, pid, packageName, attributionTag, listenerId); 55 } 56 57 /** 58 * Returns a CallerIdentity with PID and listener ID information stripped. This is mostly 59 * useful for aggregating information for debug purposes, and should not be used in any API with 60 * security requirements. 61 */ forAggregation(CallerIdentity callerIdentity)62 public static CallerIdentity forAggregation(CallerIdentity callerIdentity) { 63 if (callerIdentity.getPid() == 0 && callerIdentity.getListenerId() == null) { 64 return callerIdentity; 65 } 66 67 return new CallerIdentity(callerIdentity.getUid(), 0, callerIdentity.getPackageName(), 68 callerIdentity.getAttributionTag(), null); 69 } 70 71 /** 72 * Creates a CallerIdentity for the current process and context. 73 */ fromContext(Context context)74 public static CallerIdentity fromContext(Context context) { 75 return new CallerIdentity(Process.myUid(), Process.myPid(), context.getPackageName(), 76 context.getAttributionTag(), null); 77 } 78 79 /** 80 * Creates a CallerIdentity from the current binder identity, using the given package and 81 * feature id. The package will be checked to enforce it belongs to the calling uid, and a 82 * security exception will be thrown if it is invalid. 83 */ fromBinder(Context context, String packageName, @Nullable String attributionTag)84 public static CallerIdentity fromBinder(Context context, String packageName, 85 @Nullable String attributionTag) { 86 return fromBinder(context, packageName, attributionTag, null); 87 } 88 89 /** 90 * Creates a CallerIdentity from the current binder identity, using the given package, feature 91 * id, and listener id. The package will be checked to enforce it belongs to the calling uid, 92 * and a security exception will be thrown if it is invalid. 93 */ fromBinder(Context context, String packageName, @Nullable String attributionTag, @Nullable String listenerId)94 public static CallerIdentity fromBinder(Context context, String packageName, 95 @Nullable String attributionTag, @Nullable String listenerId) { 96 int uid = Binder.getCallingUid(); 97 if (!ArrayUtils.contains(context.getPackageManager().getPackagesForUid(uid), packageName)) { 98 throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid); 99 } 100 101 return fromBinderUnsafe(packageName, attributionTag, listenerId); 102 } 103 104 /** 105 * Creates a CallerIdentity from the current binder identity, using the given package and 106 * feature id. The package will not be checked to enforce that it belongs to the calling uid - 107 * this method should only be used if the package will be validated by some other means, such as 108 * an appops call. 109 */ fromBinderUnsafe(String packageName, @Nullable String attributionTag)110 public static CallerIdentity fromBinderUnsafe(String packageName, 111 @Nullable String attributionTag) { 112 return fromBinderUnsafe(packageName, attributionTag, null); 113 } 114 115 /** 116 * Creates a CallerIdentity from the current binder identity, using the given package, feature 117 * id, and listener id. The package will not be checked to enforce that it belongs to the 118 * calling uid - this method should only be used if the package will be validated by some other 119 * means, such as an appops call. 120 */ fromBinderUnsafe(String packageName, @Nullable String attributionTag, @Nullable String listenerId)121 public static CallerIdentity fromBinderUnsafe(String packageName, 122 @Nullable String attributionTag, @Nullable String listenerId) { 123 return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), 124 packageName, attributionTag, listenerId); 125 } 126 127 // in some tests these constants are loaded too early leading to an "incorrect" view of the 128 // current pid and uid. load lazily to prevent this problem in tests. 129 private static class Loader { 130 private static final int MY_UID = Process.myUid(); 131 private static final int MY_PID = Process.myPid(); 132 } 133 134 private final int mUid; 135 136 private final int mPid; 137 138 private final String mPackageName; 139 140 @Nullable private final String mAttributionTag; 141 142 @Nullable private final String mListenerId; 143 CallerIdentity(int uid, int pid, String packageName, @Nullable String attributionTag, @Nullable String listenerId)144 private CallerIdentity(int uid, int pid, String packageName, 145 @Nullable String attributionTag, @Nullable String listenerId) { 146 this.mUid = uid; 147 this.mPid = pid; 148 this.mPackageName = Objects.requireNonNull(packageName); 149 this.mAttributionTag = attributionTag; 150 this.mListenerId = listenerId; 151 } 152 153 /** The calling UID. */ getUid()154 public int getUid() { 155 return mUid; 156 } 157 158 /** The calling PID. */ getPid()159 public int getPid() { 160 return mPid; 161 } 162 163 /** The calling user. */ getUserId()164 public int getUserId() { 165 return UserHandle.getUserId(mUid); 166 } 167 168 /** The calling package name. */ getPackageName()169 public String getPackageName() { 170 return mPackageName; 171 } 172 173 /** The calling attribution tag. */ getAttributionTag()174 public String getAttributionTag() { 175 return mAttributionTag; 176 } 177 178 /** 179 * The calling listener id. A null listener id will match any other listener id for the purposes 180 * of {@link #equals(Object)}. 181 */ getListenerId()182 public String getListenerId() { 183 return mListenerId; 184 } 185 186 /** Returns true if this represents a system server identity. */ isSystemServer()187 public boolean isSystemServer() { 188 return mUid == Process.SYSTEM_UID; 189 } 190 191 /** Returns true if this identity represents the same user this code is running in. */ isMyUser()192 public boolean isMyUser() { 193 return UserHandle.getUserId(mUid) == UserHandle.getUserId(Loader.MY_UID); 194 } 195 196 /** Returns true if this identity represents the same uid this code is running in. */ isMyUid()197 public boolean isMyUid() { 198 return mUid == Loader.MY_UID; 199 } 200 201 /** 202 * Returns true if this identity represents the same process this code is running in. Returns 203 * false if the identity process is unknown. 204 */ isMyProcess()205 public boolean isMyProcess() { 206 return mPid == Loader.MY_PID; 207 } 208 209 /** 210 * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a 211 * new worksource representing this identity. 212 */ addToWorkSource(@ullable WorkSource workSource)213 public WorkSource addToWorkSource(@Nullable WorkSource workSource) { 214 if (workSource == null) { 215 return new WorkSource(mUid, mPackageName); 216 } else { 217 workSource.add(mUid, mPackageName); 218 return workSource; 219 } 220 } 221 222 @Override toString()223 public String toString() { 224 int length = 10 + mPackageName.length(); 225 if (mAttributionTag != null) { 226 length += mAttributionTag.length(); 227 } 228 229 StringBuilder builder = new StringBuilder(length); 230 builder.append(mUid).append("/").append(mPackageName); 231 if (mAttributionTag != null) { 232 builder.append("["); 233 if (mAttributionTag.startsWith(mPackageName)) { 234 builder.append(mAttributionTag.substring(mPackageName.length())); 235 } else { 236 builder.append(mAttributionTag); 237 } 238 builder.append("]"); 239 } 240 if (mListenerId != null) { 241 builder.append("/").append(HexDump.toHexString(mListenerId.hashCode())); 242 } 243 return builder.toString(); 244 } 245 246 @Override equals(Object o)247 public boolean equals(Object o) { 248 if (this == o) { 249 return true; 250 } 251 if (!(o instanceof CallerIdentity)) { 252 return false; 253 } 254 CallerIdentity that = (CallerIdentity) o; 255 return mUid == that.mUid 256 && mPid == that.mPid 257 && mPackageName.equals(that.mPackageName) 258 && Objects.equals(mAttributionTag, that.mAttributionTag) 259 && (mListenerId == null || that.mListenerId == null || mListenerId.equals( 260 that.mListenerId)); 261 } 262 263 @Override hashCode()264 public int hashCode() { 265 return Objects.hash(mUid, mPid, mPackageName, mAttributionTag); 266 } 267 } 268