GetPassive logoGetPassive
2026-09-04 · GetPassive Team · 8 min read

How to declare a bandwidth-sharing SDK on Play's Data Safety form and in Apple's privacy manifest

You have integrated a bandwidth-sharing SDK. The code is in, the disclosure and the opt-in are built, and now you are sitting in Play Console with the Data safety form open, or in Xcode looking at an empty PrivacyInfo.xcprivacy. Neither form has a box that obviously means traffic belonging to someone else passes through this device.

That is not an oversight. Both artefacts were designed around analytics and advertising SDKs — code that reads something about the user and sends it somewhere. A bandwidth SDK does close to the opposite: it sends very little about the user, and uses the device as transit for other people's requests. The forms still apply in full.

This is that walkthrough. Where the answer depends on how you configured the integration, we say so. What it does not cover is whether your app is permitted to ship the functionality at all: a separate gate, under a different policy, that a correct disclosure form does not settle.

The distinction that decides most of the Play form

Play defines both terms, and almost the whole declaration hangs off the difference. Collection is transmitting data from your app off a user's device. Sharing is transferring user data collected from your app to a third party.

Some transfers do not count as sharing, and the exception developers reach for is the service provider one: an entity that processes user data on behalf of the developer and based on the developer's instructions. That does not fit a bandwidth SDK. The vendor receives device and connection data for its own purposes — operating its own network, serving its own demand, calculating what it owes you. That is a third party acting for itself.

So the honest answer for the data types below is shared, not merely collected. "Shared" reads like an accusation, and your integration guide probably said the vendor never sells user data. Those are different claims: sharing describes where data goes, not what happens next.

The second thing to get straight is who is answering. Play's wording is direct: "If a third-party SDK or library in your app collects or shares user data, you must reflect this collection and sharing in the Data safety form." The declaration is yours. "The vendor told us it was fine" is not a declaration.

What a bandwidth SDK puts on the wire

At the level the forms ask about there are four things, and only three are user data.

A device record. An identifier the SDK generates for the install, plus SDK version, OS version and platform. This is what lets the network address one device rather than another, and attribute traffic to your app so you get paid.

The network's view of the connection. When the device connects, the receiving server sees its public IP address, as any server does. Country and network operator are derived from it, because routing decisions are made on geography and network.

Operational counters. Bytes moved, connection successes and failures, session timing. Your earnings figure comes from these.

Relayed traffic. Requests from our customers and the bytes of their responses, passing through the device. The destinations are our customers' destinations.

What is not involved: the SDK does not read the user's own network traffic, browsing, DNS queries, contacts, photos, files, calendar, messages, or device location APIs, and none of that is transmitted.

The relayed payload is third-party data in transit, not the device user's data. The user data on the form is the device record, the geography derived from the IP address, and the counters.

Play's Data safety form, field by field

The form asks two questions about the app as a whole: whether all collected user data is encrypted in transit, and whether users can request that their data is deleted. Then, for each data type you tick, it asks whether the data is collected, shared or both; whether it is processed ephemerally; whether it is required or the user can choose; and what it is used for.

Here is a completed declaration for an integration that is opt-in and off by default, where the publisher passes no account identifier to the SDK. Only the rows that change are shown; everything else is no.

Data typeCollectedSharedEphemeralRequired or optionalPurpose
Device or other IDsYesYesNoOptionalApp functionality
Location — Approximate locationYesYesNoOptionalApp functionality
App info and performance — Other app performance dataYesYesNoOptionalApp functionality

Device or other IDs. The identifier the SDK generates for the install relates to an individual device or app instance, which is what this type covers. Play also treats deriving identifier information from an IP address as collection of this type. Tick it, and mark it shared.

Approximate location. There is no IP address entry anywhere in Play's data type list, which is why this section is easy to leave blank. The address has no box; the geography derived from it does. Play defines approximate location as an area greater than or equal to three square kilometres, and lists deriving location information from an IP address among the triggers for the declaration. Omitting the row produces a declaration that does not match observable behaviour.

Other app performance data. Byte counters and connection outcomes are operational data about the SDK's own activity on the device — what the App info and performance category covers.

The fields worth slowing down on

Web browsing history. Leave it unticked. The type covers websites the user has visited, and the destinations of relayed traffic are our customers'. If an SDK in your app reads the user's own traffic, that is a different product and a different answer.

Ephemeral processing. Play's bar is data stored only in memory and retained no longer than necessary to service the request in real time. Byte counters kept so that you can be paid are retained, so the answer is no.

Required or optional. Your configuration decides this one, and a reviewer can compare it against your app. Off until the user turns it on means the user can choose, so the data is optional. On by default with a disclosure is not optional, and the form must say so.

User IDs. Pass your own account identifier to the SDK — for attribution, per-user earnings, or support — and you have added Personal info › User IDs, shared. This row tends to arrive late, because the choice that creates it is usually made long after the form was filled in.

Analytics as a purpose. Not a reflex tick. Counters used to route traffic and pay you are app functionality. If the vendor also uses them for its own product analytics, that is a second purpose and belongs on the form. Ask.

Encryption in transit. The question is about the user data you collect: here, the control and telemetry channel, not the relayed payload. Get written confirmation that it is TLS, then answer.

Deletion. You need a mechanism that exists, including for a device identifier held by a third party. Ask how a request reaches the vendor, and how long it takes, before you answer.

The Apple side

Two separate artefacts, and they must agree. The privacy manifest (PrivacyInfo.xcprivacy) is a property list inside the app or SDK bundle. The App Privacy details are the answers you give in App Store Connect, which become the nutrition label. Xcode aggregates the manifests in your project into a privacy report; that report is an input to the label, not a substitute for filling it in.

A manifest carries four keys: NSPrivacyTracking, whether the code tracks as Apple defines tracking; NSPrivacyTrackingDomains, the domains it connects to that engage in tracking; NSPrivacyCollectedDataTypes, each data type with whether it is linked to identity, whether it is used for tracking, and its purposes; and NSPrivacyAccessedAPITypes, required-reason API categories with an approved reason code for each.

Tracking domains have a runtime effect. Once domains are listed, network requests to them fail for users who have not granted permission through App Tracking Transparency, so adding an SDK's control endpoint defensively will break it for most of your installed base. The key covers domains the code connects to for tracking, not the destinations of relayed traffic, which are not yours and are not tracking.

Required-reason APIs cover five categories: file timestamps, system boot time, disk space, active keyboards, and user defaults. If code in your app or a dependency touches one, the manifest must name the category and give one of Apple's approved reason codes, copied exactly from Apple's current table. The codes are narrower than they look — an app reading its own user defaults and a third-party SDK wrapping the same API use different ones — and an invalid code is a submission failure rather than a policy argument.

A manifest for an app whose only unusual dependency is a bandwidth SDK looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>NSPrivacyTracking</key>
  <false/>
  <key>NSPrivacyTrackingDomains</key>
  <array/>
  <key>NSPrivacyCollectedDataTypes</key>
  <array>
    <dict>
      <key>NSPrivacyCollectedDataType</key>
      <string>NSPrivacyCollectedDataTypeDeviceID</string>
      <key>NSPrivacyCollectedDataTypeLinked</key>
      <false/>
      <key>NSPrivacyCollectedDataTypeTracking</key>
      <false/>
      <key>NSPrivacyCollectedDataTypePurposes</key>
      <array>
        <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
      </array>
    </dict>
    <dict>
      <key>NSPrivacyCollectedDataType</key>
      <string>NSPrivacyCollectedDataTypeCoarseLocation</string>
      <key>NSPrivacyCollectedDataTypeLinked</key>
      <false/>
      <key>NSPrivacyCollectedDataTypeTracking</key>
      <false/>
      <key>NSPrivacyCollectedDataTypePurposes</key>
      <array>
        <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
      </array>
    </dict>
    <dict>
      <key>NSPrivacyCollectedDataType</key>
      <string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
      <key>NSPrivacyCollectedDataTypeLinked</key>
      <false/>
      <key>NSPrivacyCollectedDataTypeTracking</key>
      <false/>
      <key>NSPrivacyCollectedDataTypePurposes</key>
      <array>
        <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
      </array>
    </dict>
  </array>
  <key>NSPrivacyAccessedAPITypes</key>
  <array>
    <dict>
      <key>NSPrivacyAccessedAPIType</key>
      <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
      <key>NSPrivacyAccessedAPITypeReasons</key>
      <array>
        <string>CA92.1</string>
      </array>
    </dict>
  </array>
</dict>
</plist>

Linked is false only while no account identifier ties the device record to a person. Pass your own user ID to the SDK and it becomes true, and an NSPrivacyCollectedDataTypeUserID entry joins the list. Same change, both stores, same day.

Apple requires a privacy manifest for SDKs on its published list of commonly used SDKs, and a signature where such an SDK ships as a binary dependency. A bandwidth SDK is unlikely to be on that list. Ask for both regardless: you cannot answer for your own app without knowing what the dependency declares, and a vendor who will not produce one has told you something useful.

When the declaration does not match the behaviour

On Play, an inaccurate declaration is a problem in its own right, separately from the underlying behaviour. Google's position is that you alone are responsible for making complete and accurate declarations, and that apps which do not become compliant are subject to enforcement including blocked updates or removal from Google Play. Omission is the easy failure here, because no box says proxy.

On Apple it surfaces earlier and more mechanically: at review, in the privacy report when aggregated manifests do not match the label you filed, or at runtime when a tracking-domain entry starts failing connections you needed.

The third consequence is not enforced by either store. Your declaration is public, and it is the first thing a security researcher or a prospective customer's compliance team reads about the integration. A precise one answers better than a blank one.

Keeping it accurate after launch

Three changes require you to revisit the form: passing your own user identifier to the SDK, moving from opt-in to on-by-default, and any change to what the SDK transmits.

The first two are yours. The third is ours, and telling you before it ships is the job, not a favour. If you are preparing a submission, ask us for a written statement covering the SDK version you are shipping, what it transmits, to whom, and for what purposes, with a date on it. A reviewer can act on that. Nobody can act on a marketing page.

Read more

All posts