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