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