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