-
Notifications
You must be signed in to change notification settings - Fork 99
feat(pans): integrate PANS metrics collection with OpenTelemetry #1467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
namanONcode
wants to merge
7
commits into
open-telemetry:main
Choose a base branch
from
namanONcode:Integrate-Android-PANS-Metrics-with-OpenTelemetry-#845
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
b51f91b
feat(pans): integrate PANS metrics collection with OpenTelemetry
namanONcode 7bc073a
test(pans): enhance unit tests for PANS metrics collector and instrum…
namanONcode 2c6578e
test(pans): add comprehensive tests for PansInstrumentation and PansM…
namanONcode bb3109b
refactor(tests): improve code formatting and readability in PANS metr…
namanONcode 98dc332
fix(connectivity): remove unnecessary null check for getNetworkCapabi…
namanONcode be25e94
refactor(tests): replace RuntimeException with error in PansMetricsCo…
namanONcode 01d48ac
feat: Introduce comprehensive PANS instrumentation analysis and plann…
namanONcode File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| public final class io/opentelemetry/android/instrumentation/pans/AppNetworkUsage { | ||
| public fun <init> (Ljava/lang/String;ILjava/lang/String;JJLio/opentelemetry/api/common/Attributes;)V | ||
| public final fun component1 ()Ljava/lang/String; | ||
| public final fun component2 ()I | ||
| public final fun component3 ()Ljava/lang/String; | ||
| public final fun component4 ()J | ||
| public final fun component5 ()J | ||
| public final fun component6 ()Lio/opentelemetry/api/common/Attributes; | ||
| public final fun copy (Ljava/lang/String;ILjava/lang/String;JJLio/opentelemetry/api/common/Attributes;)Lio/opentelemetry/android/instrumentation/pans/AppNetworkUsage; | ||
| public static synthetic fun copy$default (Lio/opentelemetry/android/instrumentation/pans/AppNetworkUsage;Ljava/lang/String;ILjava/lang/String;JJLio/opentelemetry/api/common/Attributes;ILjava/lang/Object;)Lio/opentelemetry/android/instrumentation/pans/AppNetworkUsage; | ||
| public fun equals (Ljava/lang/Object;)Z | ||
| public final fun getAttributes ()Lio/opentelemetry/api/common/Attributes; | ||
| public final fun getBytesReceived ()J | ||
| public final fun getBytesTransmitted ()J | ||
| public final fun getNetworkType ()Ljava/lang/String; | ||
| public final fun getPackageName ()Ljava/lang/String; | ||
| public final fun getUid ()I | ||
| public fun hashCode ()I | ||
| public fun toString ()Ljava/lang/String; | ||
| } | ||
|
|
||
| public final class io/opentelemetry/android/instrumentation/pans/NetworkAvailability { | ||
| public fun <init> (Ljava/lang/String;ZILio/opentelemetry/api/common/Attributes;)V | ||
| public synthetic fun <init> (Ljava/lang/String;ZILio/opentelemetry/api/common/Attributes;ILkotlin/jvm/internal/DefaultConstructorMarker;)V | ||
| public final fun component1 ()Ljava/lang/String; | ||
| public final fun component2 ()Z | ||
| public final fun component3 ()I | ||
| public final fun component4 ()Lio/opentelemetry/api/common/Attributes; | ||
| public final fun copy (Ljava/lang/String;ZILio/opentelemetry/api/common/Attributes;)Lio/opentelemetry/android/instrumentation/pans/NetworkAvailability; | ||
| public static synthetic fun copy$default (Lio/opentelemetry/android/instrumentation/pans/NetworkAvailability;Ljava/lang/String;ZILio/opentelemetry/api/common/Attributes;ILjava/lang/Object;)Lio/opentelemetry/android/instrumentation/pans/NetworkAvailability; | ||
| public fun equals (Ljava/lang/Object;)Z | ||
| public final fun getAttributes ()Lio/opentelemetry/api/common/Attributes; | ||
| public final fun getNetworkType ()Ljava/lang/String; | ||
| public final fun getSignalStrength ()I | ||
| public fun hashCode ()I | ||
| public final fun isAvailable ()Z | ||
| public fun toString ()Ljava/lang/String; | ||
| } | ||
|
|
||
| public final class io/opentelemetry/android/instrumentation/pans/PANSMetrics { | ||
| public fun <init> ()V | ||
| public fun <init> (Ljava/util/List;Ljava/util/List;Ljava/util/List;)V | ||
| public synthetic fun <init> (Ljava/util/List;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V | ||
| public final fun component1 ()Ljava/util/List; | ||
| public final fun component2 ()Ljava/util/List; | ||
| public final fun component3 ()Ljava/util/List; | ||
| public final fun copy (Ljava/util/List;Ljava/util/List;Ljava/util/List;)Lio/opentelemetry/android/instrumentation/pans/PANSMetrics; | ||
| public static synthetic fun copy$default (Lio/opentelemetry/android/instrumentation/pans/PANSMetrics;Ljava/util/List;Ljava/util/List;Ljava/util/List;ILjava/lang/Object;)Lio/opentelemetry/android/instrumentation/pans/PANSMetrics; | ||
| public fun equals (Ljava/lang/Object;)Z | ||
| public final fun getAppNetworkUsage ()Ljava/util/List; | ||
| public final fun getNetworkAvailability ()Ljava/util/List; | ||
| public final fun getPreferenceChanges ()Ljava/util/List; | ||
| public fun hashCode ()I | ||
| public fun toString ()Ljava/lang/String; | ||
| } | ||
|
|
||
| public final class io/opentelemetry/android/instrumentation/pans/PansInstrumentation : io/opentelemetry/android/instrumentation/AndroidInstrumentation { | ||
| public static final field Companion Lio/opentelemetry/android/instrumentation/pans/PansInstrumentation$Companion; | ||
| public fun <init> ()V | ||
| public fun getName ()Ljava/lang/String; | ||
| public fun install (Lio/opentelemetry/android/instrumentation/InstallationContext;)V | ||
| } | ||
|
|
||
| public final class io/opentelemetry/android/instrumentation/pans/PansInstrumentation$Companion { | ||
| } | ||
|
|
||
| public final class io/opentelemetry/android/instrumentation/pans/PreferenceChange { | ||
| public fun <init> (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLio/opentelemetry/api/common/Attributes;)V | ||
| public synthetic fun <init> (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLio/opentelemetry/api/common/Attributes;ILkotlin/jvm/internal/DefaultConstructorMarker;)V | ||
| public final fun component1 ()Ljava/lang/String; | ||
| public final fun component2 ()I | ||
| public final fun component3 ()Ljava/lang/String; | ||
| public final fun component4 ()Ljava/lang/String; | ||
| public final fun component5 ()J | ||
| public final fun component6 ()Lio/opentelemetry/api/common/Attributes; | ||
| public final fun copy (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLio/opentelemetry/api/common/Attributes;)Lio/opentelemetry/android/instrumentation/pans/PreferenceChange; | ||
| public static synthetic fun copy$default (Lio/opentelemetry/android/instrumentation/pans/PreferenceChange;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLio/opentelemetry/api/common/Attributes;ILjava/lang/Object;)Lio/opentelemetry/android/instrumentation/pans/PreferenceChange; | ||
| public fun equals (Ljava/lang/Object;)Z | ||
| public final fun getAttributes ()Lio/opentelemetry/api/common/Attributes; | ||
| public final fun getNewPreference ()Ljava/lang/String; | ||
| public final fun getOldPreference ()Ljava/lang/String; | ||
| public final fun getPackageName ()Ljava/lang/String; | ||
| public final fun getTimestamp ()J | ||
| public final fun getUid ()I | ||
| public fun hashCode ()I | ||
| public fun toString ()Ljava/lang/String; | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| plugins { | ||
| id("otel.android-library-conventions") | ||
| id("otel.publish-conventions") | ||
| id("jacoco") | ||
| } | ||
|
|
||
| description = "OpenTelemetry Android PANS (Per-Application Network Selection) instrumentation" | ||
|
|
||
| android { | ||
| namespace = "io.opentelemetry.android.instrumentation.pans" | ||
|
|
||
| defaultConfig { | ||
| consumerProguardFiles("consumer-rules.pro") | ||
| } | ||
|
|
||
| testOptions { | ||
| unitTests.isReturnDefaultValues = true | ||
| unitTests.isIncludeAndroidResources = true | ||
| } | ||
| } | ||
|
|
||
| dependencies { | ||
| api(platform(libs.opentelemetry.platform.alpha)) // Required for sonatype publishing | ||
| implementation(project(":instrumentation:android-instrumentation")) | ||
| implementation(project(":services")) | ||
| implementation(project(":common")) | ||
| implementation(project(":agent-api")) | ||
| implementation(libs.androidx.core) | ||
| implementation(libs.opentelemetry.semconv.incubating) | ||
| implementation(libs.opentelemetry.sdk) | ||
| implementation(libs.opentelemetry.instrumentation.api) | ||
| implementation(libs.auto.service.annotations) | ||
|
|
||
| ksp(libs.auto.service.processor) | ||
|
|
||
| testImplementation(project(":test-common")) | ||
| testImplementation(project(":session")) | ||
| testImplementation(libs.robolectric) | ||
| testImplementation(libs.androidx.test.core) | ||
| testImplementation(libs.mockk) | ||
| } | ||
|
|
||
| // Jacoco coverage configuration | ||
| jacoco { | ||
| toolVersion = "0.8.8" | ||
| } | ||
|
|
||
| tasks.register("jacocoTestReport") { | ||
| dependsOn("testDebugUnitTest") | ||
|
|
||
| doLast { | ||
| println("✅ Jacoco Test Report Generated") | ||
| println("📊 Coverage Report Location: build/reports/coverage/") | ||
| } | ||
| } | ||
|
|
||
| // Task to check coverage | ||
| tasks.register("checkCoverage") { | ||
| dependsOn("jacocoTestReport") | ||
|
|
||
| doLast { | ||
| println( | ||
| """ | ||
| ╔════════════════════════════════════════════════════════════════╗ | ||
| ║ PANS INSTRUMENTATION TEST COVERAGE ║ | ||
| ║ Target: 80% Coverage ║ | ||
| ╚════════════════════════════════════════════════════════════════╝ | ||
| """.trimIndent(), | ||
| ) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Keep the PANS instrumentation classes | ||
| -keep class io.opentelemetry.android.instrumentation.pans.** { *; } | ||
| -keepnames class io.opentelemetry.android.instrumentation.pans.** { *; } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| xmlns:tools="http://schemas.android.com/tools"> | ||
|
|
||
| <!-- Required to access network statistics data --> | ||
| <!-- This permission is intended for system/automotive apps --> | ||
| <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" | ||
| tools:ignore="ProtectedPermissions" /> | ||
|
|
||
| <!-- Required to check current network state and changes --> | ||
| <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||
|
|
||
| <!-- Required for internet connectivity --> | ||
| <uses-permission android:name="android.permission.INTERNET" /> | ||
|
|
||
| </manifest> | ||
|
|
||
122 changes: 122 additions & 0 deletions
122
...src/main/java/io/opentelemetry/android/instrumentation/pans/ConnectivityManagerWrapper.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.android.instrumentation.pans | ||
|
|
||
| import android.content.Context | ||
| import android.content.pm.PackageManager | ||
| import android.net.ConnectivityManager | ||
| import android.net.NetworkCapabilities | ||
| import android.util.Log | ||
| import androidx.annotation.RequiresApi | ||
| import androidx.core.content.ContextCompat | ||
|
|
||
| /** | ||
| * Wrapper around Android's ConnectivityManager for monitoring network state and preferences. | ||
| * This class provides utilities to detect available networks and their capabilities. | ||
| * Note: Most methods require API level 23+ for proper functionality. | ||
| */ | ||
| @RequiresApi(23) | ||
| internal class ConnectivityManagerWrapper( | ||
| private val context: Context, | ||
| ) { | ||
| private val connectivityManager = | ||
| context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager | ||
|
|
||
| /** | ||
| * Checks if a specific network capability is available. | ||
| * OEM_PAID and OEM_PRIVATE are network capabilities that indicate OEM-managed networks. | ||
| */ | ||
| fun hasNetworkCapability(capabilityType: Int): Boolean { | ||
| return try { | ||
| val network = connectivityManager?.activeNetwork ?: return false | ||
| val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false | ||
| capabilities.hasCapability(capabilityType) | ||
| } catch (e: Exception) { | ||
| Log.w(TAG, "Error checking network capability: $capabilityType", e) | ||
| false | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Gets all available networks with their capabilities. | ||
| */ | ||
|
namanONcode marked this conversation as resolved.
|
||
| @android.annotation.SuppressLint("WrongConstant") | ||
| fun getAvailableNetworks(): List<NetworkInfo> { | ||
| val networks = mutableListOf<NetworkInfo>() | ||
| return try { | ||
| val allNetworks = connectivityManager?.allNetworks ?: return networks | ||
| allNetworks.forEach { network -> | ||
| try { | ||
| val capabilities = connectivityManager.getNetworkCapabilities(network) | ||
| if (capabilities != null) { | ||
| networks.add( | ||
| NetworkInfo( | ||
| isOemPaid = capabilities.hasCapability(CAP_OEM_PAID), | ||
| isOemPrivate = capabilities.hasCapability(CAP_OEM_PRIVATE), | ||
| isMetered = !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED), | ||
| isConnected = isNetworkConnected(network), | ||
| ), | ||
| ) | ||
| } | ||
| } catch (e: Exception) { | ||
| Log.w(TAG, "Error getting network capabilities", e) | ||
| } | ||
| } | ||
| networks | ||
| } catch (e: Exception) { | ||
| Log.e(TAG, "Error getting available networks", e) | ||
| networks | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Checks if a specific network is currently connected. | ||
| */ | ||
| fun isNetworkConnected(network: android.net.Network): Boolean = | ||
| try { | ||
| val capabilities = connectivityManager?.getNetworkCapabilities(network) | ||
| capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false | ||
| } catch (e: Exception) { | ||
| Log.w(TAG, "Error checking network connection", e) | ||
| false | ||
| } | ||
|
|
||
| /** | ||
| * Gets the active network or null if none is active. | ||
| */ | ||
| fun getActiveNetwork(): android.net.Network? = | ||
| try { | ||
| connectivityManager?.activeNetwork | ||
| } catch (e: Exception) { | ||
| Log.w(TAG, "Error getting active network", e) | ||
| null | ||
| } | ||
|
|
||
| /** | ||
| * Checks if ACCESS_NETWORK_STATE permission is granted. | ||
| */ | ||
| fun hasAccessNetworkStatePermission(): Boolean = | ||
| ContextCompat.checkSelfPermission( | ||
| context, | ||
| "android.permission.ACCESS_NETWORK_STATE", | ||
| ) == PackageManager.PERMISSION_GRANTED | ||
|
|
||
| data class NetworkInfo( | ||
| val isOemPaid: Boolean = false, | ||
| val isOemPrivate: Boolean = false, | ||
| val isMetered: Boolean = false, | ||
| val isConnected: Boolean = false, | ||
| ) | ||
|
|
||
| companion object { | ||
| private const val TAG = "ConnMgrWrapper" | ||
|
|
||
| // Network capability constants for OEM networks | ||
| // These are defined as constants to support various Android versions | ||
| private const val CAP_OEM_PAID = 19 // NET_CAPABILITY_OEM_PAID | ||
| private const val CAP_OEM_PRIVATE = 20 // NET_CAPABILITY_OEM_PRIVATE | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment states that
PACKAGE_USAGE_STATSpermission is "intended for system/automotive apps" (line 6), but the PR description doesn't specify that this instrumentation is only for system/automotive apps.This is a protected permission that normal apps cannot obtain through runtime permission requests. This should be clearly documented in the module's README or main documentation that:
This is important for setting correct expectations for users of this library.