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