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 android.content;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemApi;
24 import android.annotation.TestApi;
25 import android.app.ActivityThread;
26 import android.app.AppGlobals;
27 import android.os.Binder;
28 import android.os.Build;
29 import android.os.IBinder;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.os.Process;
33 import android.os.UserHandle;
34 import android.permission.PermissionManager;
35 import android.permission.flags.Flags;
36 import android.util.ArraySet;
37 
38 import com.android.internal.annotations.Immutable;
39 
40 import java.util.Arrays;
41 import java.util.Collections;
42 import java.util.Objects;
43 import java.util.Set;
44 
45 /**
46  * This class represents a source to which access to permission protected data should be
47  * attributed. Attribution sources can be chained to represent cases where the protected
48  * data would flow through several applications. For example, app A may ask app B for
49  * contacts and in turn app B may ask app C for contacts. In this case, the attribution
50  * chain would be A -> B -> C and the data flow would be C -> B -> A. There are two
51  * main benefits of using the attribution source mechanism: avoid doing explicit permission
52  * checks on behalf of the calling app if you are accessing private data on their behalf
53  * to send back; avoid double data access blaming which happens as you check the calling
54  * app's permissions and when you access the data behind these permissions (for runtime
55  * permissions). Also if not explicitly blaming the caller the data access would be
56  * counted towards your app vs to the previous app where yours was just a proxy.
57  * <p>
58  * Every {@link Context} has an attribution source and you can get it via {@link
59  * Context#getAttributionSource()} representing itself, which is a chain of one. You
60  * can attribute work to another app, or more precisely to a chain of apps, through
61  * which the data you would be accessing would flow, via {@link Context#createContext(
62  * ContextParams)} plus specifying an attribution source for the next app to receive
63  * the protected data you are accessing via {@link AttributionSource.Builder#setNext(
64  * AttributionSource)}. Creating this attribution chain ensures that the datasource would
65  * check whether every app in the attribution chain has permission to access the data
66  * before releasing it. The datasource will also record appropriately that this data was
67  * accessed by the apps in the sequence if the data is behind a sensitive permission
68  * (e.g. dangerous). Again, this is useful if you are accessing the data on behalf of another
69  * app, for example a speech recognizer using the mic so it can provide recognition to
70  * a calling app.
71  * <p>
72  * You can create an attribution chain of you and any other app without any verification
73  * as this is something already available via the {@link android.app.AppOpsManager} APIs.
74  * This is supported to handle cases where you don't have access to the caller's attribution
75  * source and you can directly use the {@link AttributionSource.Builder} APIs. However,
76  * if the data flows through more than two apps (more than you access the data for the
77  * caller) you need to have a handle to the {@link AttributionSource} for the calling app's
78  * context in order to create an attribution context. This means you either need to have an
79  * API for the other app to send you its attribution source or use a platform API that pipes
80  * the callers attribution source.
81  * <p>
82  * You cannot forge an attribution chain without the participation of every app in the
83  * attribution chain (aside of the special case mentioned above). To create an attribution
84  * source that is trusted you need to create an attribution context that points to an
85  * attribution source that was explicitly created by the app that it refers to, recursively.
86  * <p>
87  * Since creating an attribution context leads to all permissions for apps in the attribution
88  * chain being checked, you need to expect getting a security exception when accessing
89  * permission protected APIs since some app in the chain may not have the permission.
90  */
91 @Immutable
92 public final class AttributionSource implements Parcelable {
93     private static final String DESCRIPTOR = "android.content.AttributionSource";
94 
95     private static final Binder sDefaultToken = new Binder(DESCRIPTOR);
96 
97     private final @NonNull AttributionSourceState mAttributionSourceState;
98 
99     private @Nullable AttributionSource mNextCached;
100     private @Nullable Set<String> mRenouncedPermissionsCached;
101 
102     /** @hide */
103     @TestApi
AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag)104     public AttributionSource(int uid, @Nullable String packageName,
105             @Nullable String attributionTag) {
106         this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken);
107     }
108 
109     /** @hide */
AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId)110     public AttributionSource(int uid, @Nullable String packageName,
111             @Nullable String attributionTag, int virtualDeviceId) {
112         this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken, null,
113                 virtualDeviceId, null);
114     }
115 
116     /** @hide */
AttributionSource(int uid, int pid, @Nullable String packageName, @Nullable String attributionTag)117     public AttributionSource(int uid, int pid, @Nullable String packageName,
118             @Nullable String attributionTag) {
119         this(uid, pid, packageName, attributionTag, sDefaultToken);
120     }
121 
122     /** @hide */
123     @TestApi
AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag, @NonNull IBinder token)124     public AttributionSource(int uid, @Nullable String packageName,
125             @Nullable String attributionTag, @NonNull IBinder token) {
126         this(uid, Process.INVALID_PID, packageName, attributionTag, token,
127                 /*renouncedPermissions*/ null, Context.DEVICE_ID_DEFAULT, /*next*/ null);
128     }
129 
130     /** @hide */
AttributionSource(int uid, int pid, @Nullable String packageName, @Nullable String attributionTag, @NonNull IBinder token)131     public AttributionSource(int uid, int pid, @Nullable String packageName,
132             @Nullable String attributionTag, @NonNull IBinder token) {
133         this(uid, pid, packageName, attributionTag, token, /*renouncedPermissions*/ null,
134                 Context.DEVICE_ID_DEFAULT, /*next*/ null);
135     }
136 
137     /** @hide */
138     @TestApi
AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable Set<String> renouncedPermissions, @Nullable AttributionSource next)139     public AttributionSource(int uid, @Nullable String packageName,
140             @Nullable String attributionTag, @Nullable Set<String> renouncedPermissions,
141             @Nullable AttributionSource next) {
142         this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken,
143                 (renouncedPermissions != null)
144                 ? renouncedPermissions.toArray(new String[0]) : null, Context.DEVICE_ID_DEFAULT,
145                 /*next*/ next);
146     }
147 
148     /** @hide */
AttributionSource(@onNull AttributionSource current, @Nullable AttributionSource next)149     public AttributionSource(@NonNull AttributionSource current, @Nullable AttributionSource next) {
150         this(current.getUid(), current.getPid(), current.getPackageName(),
151                 current.getAttributionTag(), current.getToken(),
152                 current.mAttributionSourceState.renouncedPermissions, current.getDeviceId(), next);
153     }
154 
155     /** @hide */
AttributionSource(int uid, int pid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] renouncedPermissions, int deviceId, @Nullable AttributionSource next)156     public AttributionSource(int uid, int pid, @Nullable String packageName,
157             @Nullable String attributionTag, @Nullable String[] renouncedPermissions, int deviceId,
158             @Nullable AttributionSource next) {
159         this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, deviceId,
160                 next);
161     }
162 
163     /** @hide */
164     @TestApi
165     @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
AttributionSource(int uid, int pid, @Nullable String packageName, @Nullable String attributionTag, @NonNull IBinder token, @Nullable String[] renouncedPermissions, int deviceId, @Nullable AttributionSource next)166     public AttributionSource(int uid, int pid, @Nullable String packageName,
167             @Nullable String attributionTag, @NonNull IBinder token,
168             @Nullable String[] renouncedPermissions,
169             int deviceId, @Nullable AttributionSource next) {
170         mAttributionSourceState = new AttributionSourceState();
171         mAttributionSourceState.uid = uid;
172         mAttributionSourceState.pid = pid;
173         mAttributionSourceState.token = token;
174         mAttributionSourceState.packageName = packageName;
175         mAttributionSourceState.attributionTag = attributionTag;
176         mAttributionSourceState.renouncedPermissions = renouncedPermissions;
177         mAttributionSourceState.deviceId = deviceId;
178         mAttributionSourceState.next = (next != null) ? new AttributionSourceState[]
179                 {next.mAttributionSourceState} : new AttributionSourceState[0];
180     }
181 
AttributionSource(@onNull Parcel in)182     AttributionSource(@NonNull Parcel in) {
183         this(AttributionSourceState.CREATOR.createFromParcel(in));
184 
185         if (!Binder.isDirectlyHandlingTransaction()) {
186             throw new SecurityException("AttributionSource should be unparceled during a binder "
187                     + "transaction for proper verification.");
188         }
189 
190         // Since we just unpacked this object as part of it transiting a Binder
191         // call, this is the perfect time to enforce that its UID and PID can be trusted
192         enforceCallingUid();
193 
194         // If this object is being constructed as part of a oneway Binder call, getCallingPid will
195         // return 0 instead of the true PID. In that case, invalidate the PID by setting it to
196         // INVALID_PID (-1).
197         final int callingPid = Binder.getCallingPid();
198         if (callingPid == 0) {
199             mAttributionSourceState.pid = Process.INVALID_PID;
200         }
201 
202         enforceCallingPid();
203     }
204 
205     /** @hide */
AttributionSource(@onNull AttributionSourceState attributionSourceState)206     public AttributionSource(@NonNull AttributionSourceState attributionSourceState) {
207         mAttributionSourceState = attributionSourceState;
208     }
209 
210     /** @hide */
withNextAttributionSource(@ullable AttributionSource next)211     public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
212         return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
213                 getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), next);
214     }
215 
216     /** @hide */
withPackageName(@ullable String packageName)217     public AttributionSource withPackageName(@Nullable String packageName) {
218         return new AttributionSource(getUid(), getPid(), packageName, getAttributionTag(),
219                getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
220     }
221 
222     /** @hide */
withToken(@onNull IBinder token)223     public AttributionSource withToken(@NonNull IBinder token) {
224         return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
225                 token, mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
226     }
227 
228     /** @hide */
withDefaultToken()229     public AttributionSource withDefaultToken() {
230         return withToken(sDefaultToken);
231     }
232 
233     /** @hide */
withPid(int pid)234     public AttributionSource withPid(int pid) {
235         return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(),
236                 getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
237     }
238 
239     /** @hide */
withDeviceId(int deviceId)240     public AttributionSource withDeviceId(int deviceId) {
241         return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
242                 getToken(), mAttributionSourceState.renouncedPermissions, deviceId, getNext());
243     }
244 
245     /** @hide */
asState()246     public @NonNull AttributionSourceState asState() {
247         return mAttributionSourceState;
248     }
249 
250     /** @hide */
asScopedParcelState()251     public @NonNull ScopedParcelState asScopedParcelState() {
252         return new ScopedParcelState(this);
253     }
254 
255     /**
256      * Returns a generic {@link AttributionSource} that represents the entire
257      * calling process.
258      *
259      * <p>Callers are <em>strongly</em> encouraged to use a more specific
260      * attribution source whenever possible, such as from
261      * {@link Context#getAttributionSource()}, since that enables developers to
262      * have more detailed and scoped control over attribution within
263      * sub-components of their app.
264      *
265      * @see Context#createAttributionContext(String)
266      * @see Context#getAttributionTag()
267      * @return a generic {@link AttributionSource} representing the entire
268      *         calling process
269      * @throws IllegalStateException when no accurate {@link AttributionSource}
270      *         can be determined
271      */
myAttributionSource()272     public static @NonNull AttributionSource myAttributionSource() {
273 
274         final AttributionSource globalSource = ActivityThread.currentAttributionSource();
275         if (globalSource != null) {
276             return globalSource;
277         }
278 
279         int uid = Process.myUid();
280         if (uid == Process.ROOT_UID) {
281             uid = Process.SYSTEM_UID;
282         }
283         try {
284             return new AttributionSource.Builder(uid)
285                 .setPid(Process.myPid())
286                 .setDeviceId(Context.DEVICE_ID_DEFAULT)
287                 .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
288                 .build();
289         } catch (Exception ignored) {
290         }
291 
292         throw new IllegalStateException("Failed to resolve AttributionSource");
293     }
294 
295     /**
296      * This is a scoped object that exposes the content of an attribution source
297      * as a parcel. This is useful when passing one to native and avoid custom
298      * conversion logic from Java to native state that needs to be kept in sync
299      * as attribution source evolves. This way we use the same logic for passing
300      * to native as the ones for passing in an IPC - in both cases this is the
301      * same auto generated code.
302      *
303      * @hide
304      */
305     public static class ScopedParcelState implements AutoCloseable {
306         private final Parcel mParcel;
307 
getParcel()308         public @NonNull Parcel getParcel() {
309             return mParcel;
310         }
311 
ScopedParcelState(AttributionSource attributionSource)312         public ScopedParcelState(AttributionSource attributionSource) {
313             mParcel = Parcel.obtain();
314             attributionSource.writeToParcel(mParcel, 0);
315             mParcel.setDataPosition(0);
316         }
317 
close()318         public void close() {
319             mParcel.recycle();
320         }
321     }
322 
323     /**
324      * If you are handling an IPC and you don't trust the caller you need to validate
325      * whether the attribution source is one for the calling app to prevent the caller
326      * to pass you a source from another app without including themselves in the
327      * attribution chain.
328      *
329      * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
330      */
enforceCallingUid()331     public void enforceCallingUid() {
332         if (!checkCallingUid()) {
333             throw new SecurityException("Calling uid: " + Binder.getCallingUid()
334                     + " doesn't match source uid: " + mAttributionSourceState.uid);
335         }
336         // No need to check package as app ops manager does it already.
337     }
338 
339     /**
340      * If you are handling an IPC and you don't trust the caller you need to validate
341      * whether the attribution source is one for the calling app to prevent the caller
342      * to pass you a source from another app without including themselves in the
343      * attribution chain.
344      *
345      * @return if the attribution source cannot be trusted to be from the caller.
346      */
checkCallingUid()347     public boolean checkCallingUid() {
348         final int callingUid = Binder.getCallingUid();
349         if (callingUid != Process.ROOT_UID
350                 && UserHandle.getAppId(callingUid) != Process.SYSTEM_UID
351                 && callingUid != mAttributionSourceState.uid) {
352             return false;
353         }
354         // No need to check package as app ops manager does it already.
355         return true;
356     }
357 
358     /**
359      * Validate that the pid being claimed for the calling app is not spoofed.
360      *
361      * Note that the PID may be unavailable, for example if we're in a oneway Binder call. In this
362      * case, calling enforceCallingPid is guaranteed to fail. The caller should anticipate this.
363      *
364      * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
365      * @hide
366      */
367     @TestApi
enforceCallingPid()368     public void enforceCallingPid() {
369         if (!checkCallingPid()) {
370             if (Binder.getCallingPid() == 0) {
371                 throw new SecurityException("Calling pid unavailable due to oneway Binder call.");
372             } else {
373                 throw new SecurityException("Calling pid: " + Binder.getCallingPid()
374                         + " doesn't match source pid: " + mAttributionSourceState.pid);
375             }
376         }
377     }
378 
379     /**
380      * Validate that the pid being claimed for the calling app is not spoofed
381      *
382      * @return if the attribution source cannot be trusted to be from the caller.
383      */
checkCallingPid()384     private boolean checkCallingPid() {
385         final int callingPid = Binder.getCallingPid();
386         if (mAttributionSourceState.pid != Process.INVALID_PID
387                 && callingPid != mAttributionSourceState.pid) {
388             return false;
389         }
390         return true;
391     }
392 
393     @Override
toString()394     public String toString() {
395         if (Build.IS_DEBUGGABLE) {
396             return "AttributionSource { " +
397                     "uid = " + mAttributionSourceState.uid + ", " +
398                     "packageName = " + mAttributionSourceState.packageName + ", " +
399                     "attributionTag = " + mAttributionSourceState.attributionTag + ", " +
400                     "token = " + mAttributionSourceState.token + ", " +
401                     "deviceId = " + mAttributionSourceState.deviceId + ", " +
402                     "next = " + (mAttributionSourceState.next != null
403                     && mAttributionSourceState.next.length > 0
404                     ? new AttributionSource(mAttributionSourceState.next[0]).toString() : null) +
405                     " }";
406         }
407         return super.toString();
408     }
409 
410     /**
411      * @return The next UID that would receive the permission protected data.
412      *
413      * @hide
414      */
getNextUid()415     public int getNextUid() {
416         if (mAttributionSourceState.next != null
417                 && mAttributionSourceState.next.length > 0) {
418             return mAttributionSourceState.next[0].uid;
419         }
420         return Process.INVALID_UID;
421     }
422 
423     /**
424      * @return The next package that would receive the permission protected data.
425      *
426      * @hide
427      */
getNextPackageName()428     public @Nullable String getNextPackageName() {
429         if (mAttributionSourceState.next != null
430                 && mAttributionSourceState.next.length > 0) {
431             return mAttributionSourceState.next[0].packageName;
432         }
433         return null;
434     }
435 
436     /**
437      * @return The next package's attribution tag that would receive
438      * the permission protected data.
439      *
440      * @hide
441      */
getNextAttributionTag()442     public @Nullable String getNextAttributionTag() {
443         if (mAttributionSourceState.next != null
444                 && mAttributionSourceState.next.length > 0) {
445             return mAttributionSourceState.next[0].attributionTag;
446         }
447         return null;
448     }
449 
450     /**
451      * @return The next package's token that would receive
452      * the permission protected data.
453      *
454      * @hide
455      */
getNextToken()456     public @Nullable IBinder getNextToken() {
457         if (mAttributionSourceState.next != null
458                 && mAttributionSourceState.next.length > 0) {
459             return mAttributionSourceState.next[0].token;
460         }
461         return null;
462     }
463 
464     /**
465      * @return The next package's device Id from its context.
466      * This device ID is used for permissions checking during attribution source validation.
467      *
468      * @hide
469      */
getNextDeviceId()470     public int getNextDeviceId() {
471         if (mAttributionSourceState.next != null
472                 && mAttributionSourceState.next.length > 0) {
473             return mAttributionSourceState.next[0].deviceId;
474         }
475         return Context.DEVICE_ID_DEFAULT;
476     }
477 
478     /**
479      * Checks whether this attribution source can be trusted. That is whether
480      * the app it refers to created it and provided to the attribution chain.
481      *
482      * @param context Context handle.
483      * @return Whether this is a trusted source.
484      */
isTrusted(@onNull Context context)485     public boolean isTrusted(@NonNull Context context) {
486         return mAttributionSourceState.token != null
487                 && context.getSystemService(PermissionManager.class)
488                         .isRegisteredAttributionSource(this);
489     }
490 
491     /**
492      * Permissions that should be considered revoked regardless if granted.
493      *
494      * @hide
495      */
496     @SystemApi
497     @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
498     @NonNull
getRenouncedPermissions()499     public Set<String> getRenouncedPermissions() {
500         if (mRenouncedPermissionsCached == null) {
501             if (mAttributionSourceState.renouncedPermissions != null) {
502                 mRenouncedPermissionsCached = new ArraySet<>(
503                         mAttributionSourceState.renouncedPermissions);
504             } else {
505                 mRenouncedPermissionsCached = Collections.emptySet();
506             }
507         }
508         return mRenouncedPermissionsCached;
509     }
510 
511     /**
512      * The UID that is accessing the permission protected data.
513      */
getUid()514     public int getUid() {
515         return mAttributionSourceState.uid;
516     }
517 
518     /**
519      * The PID that is accessing the permission protected data.
520      */
getPid()521     public int getPid() {
522         return mAttributionSourceState.pid;
523     }
524 
525     /**
526      * The package that is accessing the permission protected data.
527      */
getPackageName()528     public @Nullable String getPackageName() {
529         return mAttributionSourceState.packageName;
530     }
531 
532     /**
533      * The attribution tag of the app accessing the permission protected data.
534      */
getAttributionTag()535     public @Nullable String getAttributionTag() {
536         return mAttributionSourceState.attributionTag;
537     }
538 
539     /**
540      * Gets the device ID for this attribution source. Attribution source can set the device ID
541      * using {@link Builder#setDeviceId(int)}, the default device ID is
542      * {@link Context#DEVICE_ID_DEFAULT}.
543      * <p>
544      * This device ID is used for permissions checking during attribution source validation.
545      */
546     @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
getDeviceId()547     public int getDeviceId() {
548         return mAttributionSourceState.deviceId;
549     }
550 
551     /**
552      * Unique token for that source.
553      *
554      * @hide
555      */
getToken()556     public @NonNull IBinder getToken() {
557         return mAttributionSourceState.token;
558     }
559 
560     /**
561      * The next app to receive the permission protected data.
562      */
getNext()563     public @Nullable AttributionSource getNext() {
564         if (mNextCached == null && mAttributionSourceState.next != null
565                 && mAttributionSourceState.next.length > 0) {
566             mNextCached = new AttributionSource(mAttributionSourceState.next[0]);
567         }
568         return mNextCached;
569     }
570 
571     @Override
equals(@ullable Object o)572     public boolean equals(@Nullable Object o) {
573         if (this == o) return true;
574         if (o == null || getClass() != o.getClass()) return false;
575         AttributionSource that = (AttributionSource) o;
576         return equalsExceptToken(that) && Objects.equals(
577                 mAttributionSourceState.token, that.mAttributionSourceState.token);
578     }
579 
580     /**
581      * We store trusted attribution sources without their token (the token is the key to the map)
582      * to avoid having a strong reference to the token. This means, when checking the equality of a
583      * supplied AttributionSource in PermissionManagerService.isTrustedAttributionSource, we want to
584      * compare everything except the token.
585      *
586      * @hide
587      */
equalsExceptToken(@ullable AttributionSource o)588     public boolean equalsExceptToken(@Nullable AttributionSource o) {
589         if (o == null) return false;
590         return mAttributionSourceState.uid == o.mAttributionSourceState.uid
591                 && Objects.equals(mAttributionSourceState.packageName,
592                 o.mAttributionSourceState.packageName)
593                 && Objects.equals(mAttributionSourceState.attributionTag,
594                 o.mAttributionSourceState.attributionTag)
595                 && Arrays.equals(mAttributionSourceState.renouncedPermissions,
596                 o.mAttributionSourceState.renouncedPermissions)
597                 && Objects.equals(getNext(), o.getNext());
598     }
599 
600     @Override
hashCode()601     public int hashCode() {
602         return Objects.hash(mAttributionSourceState.uid, mAttributionSourceState.packageName,
603                 mAttributionSourceState.attributionTag, mAttributionSourceState.token,
604                 Arrays.hashCode(mAttributionSourceState.renouncedPermissions), getNext());
605     }
606 
607     @Override
writeToParcel(@onNull Parcel dest, int flags)608     public void writeToParcel(@NonNull Parcel dest, int flags) {
609         mAttributionSourceState.writeToParcel(dest, flags);
610     }
611 
612     @Override
describeContents()613     public int describeContents() { return 0; }
614 
615     public static final @NonNull Parcelable.Creator<AttributionSource> CREATOR
616             = new Parcelable.Creator<AttributionSource>() {
617         @Override
618         public AttributionSource[] newArray(int size) {
619             return new AttributionSource[size];
620         }
621 
622         @Override
623         public AttributionSource createFromParcel(@NonNull Parcel in) {
624             return new AttributionSource(in);
625         }
626     };
627 
628     /**
629      * A builder for {@link AttributionSource}
630      */
631     public static final class Builder {
632         private @NonNull final AttributionSourceState mAttributionSourceState =
633                 new AttributionSourceState();
634 
635         private long mBuilderFieldsSet = 0L;
636 
637         /**
638          * Creates a new Builder.
639          *
640          * @param uid
641          *   The UID that is accessing the permission protected data.
642          */
Builder(int uid)643         public Builder(int uid) {
644             mAttributionSourceState.uid = uid;
645         }
646 
Builder(@onNull AttributionSource current)647         public Builder(@NonNull AttributionSource current) {
648             if (current == null) {
649                 throw new IllegalArgumentException("current AttributionSource can not be null");
650             }
651             mAttributionSourceState.uid = current.getUid();
652             mAttributionSourceState.pid = current.getPid();
653             mAttributionSourceState.packageName = current.getPackageName();
654             mAttributionSourceState.attributionTag = current.getAttributionTag();
655             mAttributionSourceState.token = current.getToken();
656             mAttributionSourceState.renouncedPermissions =
657                 current.mAttributionSourceState.renouncedPermissions;
658         }
659 
660         /**
661          * The PID of the process that is accessing the permission protected data.
662          *
663          * If not called, pid will default to {@link Process@INVALID_PID} (-1). This indicates that
664          * the PID data is missing. Supplying a PID is not required, but recommended when
665          * accessible.
666          */
setPid(int value)667         public @NonNull Builder setPid(int value) {
668             checkNotUsed();
669             mBuilderFieldsSet |= 0x2;
670             mAttributionSourceState.pid = value;
671             return this;
672         }
673 
674         /**
675          * The package that is accessing the permission protected data.
676          */
setPackageName(@ullable String value)677         public @NonNull Builder setPackageName(@Nullable String value) {
678             checkNotUsed();
679             mBuilderFieldsSet |= 0x4;
680             mAttributionSourceState.packageName = value;
681             return this;
682         }
683 
684         /**
685          * The attribution tag of the app accessing the permission protected data.
686          */
setAttributionTag(@ullable String value)687         public @NonNull Builder setAttributionTag(@Nullable String value) {
688             checkNotUsed();
689             mBuilderFieldsSet |= 0x8;
690             mAttributionSourceState.attributionTag = value;
691             return this;
692         }
693 
694         /**
695          * Sets permissions which have been voluntarily "renounced" by the
696          * calling app.
697          * <p>
698          * Interactions performed through services obtained from the created
699          * Context will ideally be treated as if these "renounced" permissions
700          * have not actually been granted to the app, regardless of their actual
701          * grant status.
702          * <p>
703          * This is designed for use by separate logical components within an app
704          * which have no intention of interacting with data or services that are
705          * protected by the renounced permissions.
706          * <p>
707          * Note that only {@link PermissionInfo#PROTECTION_DANGEROUS}
708          * permissions are supported by this mechanism. Additionally, this
709          * mechanism only applies to calls made through services obtained via
710          * {@link Context#getSystemService}; it has no effect on static or raw
711          * Binder calls.
712          *
713          * @param renouncedPermissions The set of permissions to treat as
714          *            renounced, which is as if not granted.
715          * @return This builder.
716          * @hide
717          */
718         @SystemApi
719         @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
setRenouncedPermissions(@ullable Set<String> value)720         public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) {
721             checkNotUsed();
722             mBuilderFieldsSet |= 0x10;
723             mAttributionSourceState.renouncedPermissions = (value != null)
724                     ? value.toArray(new String[0]) : null;
725             return this;
726         }
727 
728         /**
729          * Set the device ID for this attribution source, permission check would happen
730          * against this device ID.
731          *
732          * @return the builder
733          */
734         @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
setDeviceId(int deviceId)735         public @NonNull Builder setDeviceId(int deviceId) {
736             checkNotUsed();
737             mBuilderFieldsSet |= 0x12;
738             mAttributionSourceState.deviceId = deviceId;
739             return this;
740         }
741 
742         /**
743          * The next app to receive the permission protected data.
744          */
setNext(@ullable AttributionSource value)745         public @NonNull Builder setNext(@Nullable AttributionSource value) {
746             checkNotUsed();
747             mBuilderFieldsSet |= 0x20;
748             mAttributionSourceState.next = (value != null) ? new AttributionSourceState[]
749                     {value.mAttributionSourceState} : mAttributionSourceState.next;
750             return this;
751         }
752 
753         /**
754          * The next app to receive the permission protected data.
755          */
756         @FlaggedApi(Flags.FLAG_SET_NEXT_ATTRIBUTION_SOURCE)
setNextAttributionSource(@onNull AttributionSource value)757         public @NonNull Builder setNextAttributionSource(@NonNull AttributionSource value) {
758             checkNotUsed();
759             if (value == null) {
760                 throw new IllegalArgumentException("Null AttributionSource not permitted.");
761             }
762             mBuilderFieldsSet |= 0x20;
763             mAttributionSourceState.next =
764                     new AttributionSourceState[]{value.mAttributionSourceState};
765             return this;
766         }
767 
768         /** Builds the instance. This builder should not be touched after calling this! */
build()769         public @NonNull AttributionSource build() {
770             checkNotUsed();
771             mBuilderFieldsSet |= 0x40; // Mark builder used
772 
773             if ((mBuilderFieldsSet & 0x2) == 0) {
774                 mAttributionSourceState.pid = Process.INVALID_PID;
775             }
776             if ((mBuilderFieldsSet & 0x4) == 0) {
777                 mAttributionSourceState.packageName = null;
778             }
779             if ((mBuilderFieldsSet & 0x8) == 0) {
780                 mAttributionSourceState.attributionTag = null;
781             }
782             if ((mBuilderFieldsSet & 0x10) == 0) {
783                 mAttributionSourceState.renouncedPermissions = null;
784             }
785             if ((mBuilderFieldsSet & 0x12) == 0) {
786                 mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
787             }
788             if ((mBuilderFieldsSet & 0x20) == 0) {
789                 mAttributionSourceState.next = null;
790             }
791 
792             mAttributionSourceState.token = sDefaultToken;
793 
794             if (mAttributionSourceState.next == null) {
795                 // The NDK aidl backend doesn't support null parcelable arrays.
796                 mAttributionSourceState.next = new AttributionSourceState[0];
797             }
798             return new AttributionSource(mAttributionSourceState);
799         }
800 
checkNotUsed()801         private void checkNotUsed() {
802             if ((mBuilderFieldsSet & 0x40) != 0) {
803                 throw new IllegalStateException(
804                         "This Builder should not be reused. Use a new Builder instance instead");
805             }
806         }
807     }
808 }
809