diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml
index 97b1f65042..ae7c24f8c5 100644
--- a/vending-app/src/main/AndroidManifest.xml
+++ b/vending-app/src/main/AndroidManifest.xml
@@ -193,6 +193,14 @@
+
+
+
+
+
+
= CURRENT_API_VERSION) {
+ putInt(KEY_AGE_SIGNALS_STATUS, AGE_SIGNALS_STATUS_NOT_SHARED)
+ }
+}
+
+internal fun createAgeSignalsAccessResponse(): Bundle = Bundle().apply {
+ putInt(KEY_AGE_SIGNALS_STATUS, AGE_SIGNALS_STATUS_NOT_SHARED)
+}
+
+internal fun createErrorResponse(): Bundle = Bundle().apply {
+ putInt(KEY_ERROR_CODE, ERROR_CODE_INTERNAL_ERROR)
+}
+
+class AgeSignalsService : LifecycleService() {
+
+ override fun onBind(intent: Intent): IBinder? {
+ super.onBind(intent)
+ Log.d(TAG, "onBind")
+ return AgeSignalsServiceImpl(this, lifecycle).asBinder()
+ }
+}
+
+internal fun interface CallingPackageVerifier {
+ fun verify(context: Context, packageName: String): String?
+}
+
+internal class AgeSignalsServiceImpl(
+ private val context: Context,
+ override val lifecycle: Lifecycle,
+ private val verifier: CallingPackageVerifier = CallingPackageVerifier { verifierContext, packageName ->
+ PackageUtils.getAndCheckCallingPackage(verifierContext, packageName)
+ }
+) : IAgeSignalsService.Stub(), LifecycleOwner {
+
+ override fun checkAgeSignals(packageName: String?, bundle: Bundle?, callback: IAgeSignalsServiceCallback?) {
+ if (callback == null) {
+ Log.w(TAG, "checkAgeSignals called without a callback")
+ return
+ }
+ if (packageName.isNullOrBlank()) {
+ Log.w(TAG, "checkAgeSignals called without a package name")
+ sendCheckError(callback)
+ return
+ }
+ if (!verifyCallingPackage(packageName, "checkAgeSignals", callback::onError)) return
+
+ val response = try {
+ createCheckAgeSignalsResponse(bundle?.getInt(KEY_PLAY_CORE_VERSION, 0) ?: 0)
+ } catch (e: Exception) {
+ Log.w(TAG, "Failed to create checkAgeSignals response for $packageName", e)
+ sendCheckError(callback)
+ return
+ }
+ try {
+ callback.onCompleteCheckAgeSignals(response)
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Failed to deliver checkAgeSignals response for $packageName", e)
+ }
+ }
+
+ override fun requestAgeSignalsAccess(packageName: String?, bundle: Bundle?, callback: IAgeSignalsAccessCallback?) {
+ if (callback == null) {
+ Log.w(TAG, "requestAgeSignalsAccess called without a callback")
+ return
+ }
+ if (packageName.isNullOrBlank()) {
+ Log.w(TAG, "requestAgeSignalsAccess called without a package name")
+ sendAccessError(callback)
+ return
+ }
+ if (!verifyCallingPackage(packageName, "requestAgeSignalsAccess", callback::onError)) return
+
+ val response = try {
+ createAgeSignalsAccessResponse()
+ } catch (e: Exception) {
+ Log.w(TAG, "Failed to create requestAgeSignalsAccess response for $packageName", e)
+ sendAccessError(callback)
+ return
+ }
+ try {
+ callback.onCompleteRequestAgeSignalsAccess(response)
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Failed to deliver requestAgeSignalsAccess response for $packageName", e)
+ }
+ }
+
+ private fun verifyCallingPackage(packageName: String, method: String, onError: (Bundle) -> Unit): Boolean {
+ val verifiedPackageName = try {
+ verifier.verify(context, packageName)
+ } catch (e: Exception) {
+ Log.w(TAG, "$method rejected caller for $packageName", e)
+ sendError(method, onError)
+ return false
+ }
+ if (verifiedPackageName == null) {
+ Log.w(TAG, "$method could not verify caller for $packageName")
+ sendError(method, onError)
+ return false
+ }
+ return true
+ }
+
+ private fun sendCheckError(callback: IAgeSignalsServiceCallback) {
+ sendError("checkAgeSignals", callback::onError)
+ }
+
+ private fun sendAccessError(callback: IAgeSignalsAccessCallback) {
+ sendError("requestAgeSignalsAccess", callback::onError)
+ }
+
+ private fun sendError(method: String, onError: (Bundle) -> Unit) {
+ try {
+ onError(createErrorResponse())
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Failed to deliver $method error", e)
+ }
+ }
+
+ override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =
+ warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) }
+}