1 /*
2  * Copyright 2023 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 androidx.work.impl.utils
18 
19 import android.net.NetworkCapabilities
20 import android.net.NetworkRequest
21 import android.os.Build
22 import androidx.annotation.RequiresApi
23 import androidx.work.Logger
24 
25 internal data class NetworkRequestCompat(val wrapped: Any? = null) {
26 
27     companion object {
28         val TAG = Logger.tagWithPrefix("NetworkRequestCompat")
29     }
30 
31     @get:RequiresApi(21)
32     val networkRequest: NetworkRequest?
33         get() = wrapped as NetworkRequest?
34 }
35 
36 @get:RequiresApi(28)
37 val NetworkRequest.transportTypesCompat: IntArray
38     get() =
39         if (Build.VERSION.SDK_INT >= 31) {
40             NetworkRequest31.transportTypes(this)
41         } else {
42             intArrayOf(
43                     NetworkCapabilities.TRANSPORT_BLUETOOTH,
44                     NetworkCapabilities.TRANSPORT_CELLULAR,
45                     NetworkCapabilities.TRANSPORT_ETHERNET,
46                     NetworkCapabilities.TRANSPORT_LOWPAN,
47                     NetworkCapabilities.TRANSPORT_THREAD,
48                     NetworkCapabilities.TRANSPORT_USB,
49                     NetworkCapabilities.TRANSPORT_VPN,
50                     NetworkCapabilities.TRANSPORT_WIFI,
51                     NetworkCapabilities.TRANSPORT_WIFI_AWARE,
52                 )
<lambda>null53                 .filter { NetworkRequest28.hasTransport(this, it) }
54                 .toIntArray()
55         }
56 
57 @get:RequiresApi(28)
58 val NetworkRequest.capabilitiesCompat: IntArray
59     get() =
60         if (Build.VERSION.SDK_INT >= 31) {
61             NetworkRequest31.capabilities(this)
62         } else {
63             intArrayOf(
64                     NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL,
65                     NetworkCapabilities.NET_CAPABILITY_CBS,
66                     NetworkCapabilities.NET_CAPABILITY_DUN,
67                     NetworkCapabilities.NET_CAPABILITY_EIMS,
68                     NetworkCapabilities.NET_CAPABILITY_ENTERPRISE,
69                     NetworkCapabilities.NET_CAPABILITY_FOREGROUND,
70                     NetworkCapabilities.NET_CAPABILITY_FOTA,
71                     NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT,
72                     NetworkCapabilities.NET_CAPABILITY_IA,
73                     NetworkCapabilities.NET_CAPABILITY_IMS,
74                     NetworkCapabilities.NET_CAPABILITY_INTERNET,
75                     NetworkCapabilities.NET_CAPABILITY_MCX,
76                     NetworkCapabilities.NET_CAPABILITY_MMS,
77                     NetworkCapabilities.NET_CAPABILITY_MMTEL,
78                     NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED,
79                     NetworkCapabilities.NET_CAPABILITY_NOT_METERED,
80                     NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED,
81                     NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
82                     NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED,
83                     NetworkCapabilities.NET_CAPABILITY_NOT_VPN,
84                     NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH,
85                     NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY,
86                     NetworkCapabilities.NET_CAPABILITY_RCS,
87                     NetworkCapabilities.NET_CAPABILITY_SUPL,
88                     NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED,
89                     NetworkCapabilities.NET_CAPABILITY_TRUSTED,
90                     NetworkCapabilities.NET_CAPABILITY_VALIDATED,
91                     NetworkCapabilities.NET_CAPABILITY_WIFI_P2P,
92                     NetworkCapabilities.NET_CAPABILITY_XCAP,
93                 )
<lambda>null94                 .filter { NetworkRequest28.hasCapability(this, it) }
95                 .toIntArray()
96         }
97 
98 @RequiresApi(28)
99 object NetworkRequest28 {
hasCapabilitynull100     internal fun hasCapability(request: NetworkRequest, capability: Int) =
101         request.hasCapability(capability)
102 
103     internal fun hasTransport(request: NetworkRequest, transport: Int) =
104         request.hasTransport(transport)
105 
106     @JvmStatic
107     fun createNetworkRequest(capabilities: IntArray, transports: IntArray): NetworkRequest {
108         val networkRequest = NetworkRequest.Builder()
109         capabilities.forEach {
110             try {
111                 networkRequest.addCapability(it)
112             } catch (ex: IllegalArgumentException) {
113                 // b/351180465 - Ignoring the IAE that addCapability() can throw on SDK < 35 and
114                 // aligning with newer SDK behaviour. Capabilities are persisted in the database
115                 // and the framework can by default add new ones. Catching this exception mitigates
116                 // the case where decoding the capabilities from the database fails across OS
117                 // changes.
118                 Logger.get()
119                     .warning(NetworkRequestCompat.TAG, "Ignoring adding capability '$it'", ex)
120             }
121         }
122         transports.forEach { networkRequest.addTransportType(it) }
123         return networkRequest.build()
124     }
125 
createNetworkRequestCompatnull126     internal fun createNetworkRequestCompat(
127         capabilities: IntArray,
128         transports: IntArray
129     ): NetworkRequestCompat {
130         return NetworkRequestCompat(createNetworkRequest(capabilities, transports))
131     }
132 }
133 
134 @RequiresApi(31)
135 private object NetworkRequest31 {
capabilitiesnull136     fun capabilities(request: NetworkRequest) = request.capabilities
137 
138     fun transportTypes(request: NetworkRequest) = request.transportTypes
139 }
140 
141 @RequiresApi(30)
142 internal object NetworkRequest30 {
143     fun getNetworkSpecifier(request: NetworkRequest) = request.networkSpecifier
144 }
145