• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.telephony.data;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 
24 import java.math.BigInteger;
25 import java.nio.ByteBuffer;
26 import java.util.Objects;
27 import java.util.Set;
28 import java.util.UUID;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 
32 /**
33  * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection
34  * Policy(URSP) traffic matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an
35  * optional Data Network Name(DNN), which, if present, must be used for traffic matching; it does
36  * not specify the end point to be used for the data call.
37  */
38 public final class TrafficDescriptor implements Parcelable {
39     /**
40      * The OS/App id
41      *
42      * @hide
43      */
44     public static final class OsAppId {
45         /**
46          * OSId for "Android", using UUID version 5 with namespace ISO OSI.
47          * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching.
48          */
49         public static final UUID ANDROID_OS_ID =
50                 UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47");
51 
52         /**
53          * Allowed app ids.
54          */
55         // The following app ids are the only apps id Android supports. OEMs or vendors are
56         // prohibited to modify/extend the allowed list, especially passing the real package name to
57         // the network.
58         private static final Set<String> ALLOWED_APP_IDS = Set.of(
59                 "ENTERPRISE", "PRIORITIZE_LATENCY", "PRIORITIZE_BANDWIDTH", "CBS"
60         );
61 
62         /** OS id in UUID format. */
63         private final @NonNull UUID mOsId;
64 
65         /**
66          * App id in string format. Note that Android will not allow use specific app id. This must
67          * be a category/capability identifier.
68          */
69         private final @NonNull String mAppId;
70 
71         /**
72          * The differentiator when multiple traffic descriptor has the same OS and app id. Must be
73          * greater than 1.
74          */
75         private final int mDifferentiator;
76 
77         /**
78          * Constructor
79          *
80          * @param osId OS id in UUID format.
81          * @param appId App id in string format. Note that Android will not allow use specific app
82          * id. This must be a category/capability identifier.
83          */
OsAppId(@onNull UUID osId, @NonNull String appId)84         public OsAppId(@NonNull UUID osId, @NonNull String appId) {
85             this(osId, appId, 1);
86         }
87 
88         /**
89          * Constructor
90          *
91          * @param osId OS id in UUID format.
92          * @param appId App id in string format. Note that Android will not allow use specific app
93          * id. This must be a category/capability identifier.
94          * @param differentiator The differentiator when multiple traffic descriptor has the same
95          * OS and app id. Must be greater than 0.
96          */
OsAppId(@onNull UUID osId, @NonNull String appId, int differentiator)97         public OsAppId(@NonNull UUID osId, @NonNull String appId, int differentiator) {
98             Objects.requireNonNull(osId);
99             Objects.requireNonNull(appId);
100             if (differentiator < 1) {
101                 throw new IllegalArgumentException("Invalid differentiator " + differentiator);
102             }
103 
104             mOsId = osId;
105             mAppId = appId;
106             mDifferentiator = differentiator;
107         }
108 
109         /**
110          * Constructor from raw byte array.
111          *
112          * @param rawOsAppId The raw OS/App id.
113          */
OsAppId(@onNull byte[] rawOsAppId)114         public OsAppId(@NonNull byte[] rawOsAppId) {
115             try {
116                 ByteBuffer bb = ByteBuffer.wrap(rawOsAppId);
117                 // OS id is the first 16 bytes.
118                 mOsId = new UUID(bb.getLong(), bb.getLong());
119                 // App id length is 1 byte.
120                 int appIdLen = bb.get();
121                 // The remaining is the app id + differentiator.
122                 byte[] appIdAndDifferentiator = new byte[appIdLen];
123                 bb.get(appIdAndDifferentiator, 0, appIdLen);
124                 // Extract trailing numbers, for example, "ENTERPRISE", "ENTERPRISE3".
125                 String appIdAndDifferentiatorStr = new String(appIdAndDifferentiator);
126                 Pattern pattern = Pattern.compile("[^0-9]+([0-9]+)$");
127                 Matcher matcher = pattern.matcher(new String(appIdAndDifferentiator));
128                 if (matcher.find()) {
129                     mDifferentiator = Integer.parseInt(matcher.group(1));
130                     mAppId = appIdAndDifferentiatorStr.replace(matcher.group(1), "");
131                 } else {
132                     mDifferentiator = 1;
133                     mAppId = appIdAndDifferentiatorStr;
134                 }
135             } catch (Exception e) {
136                 throw new IllegalArgumentException("Failed to decode " + (rawOsAppId != null
137                         ? new BigInteger(1, rawOsAppId).toString(16) : null));
138             }
139         }
140 
141         /**
142          * @return The OS id in UUID format.
143          */
getOsId()144         public @NonNull UUID getOsId() {
145             return mOsId;
146         }
147 
148         /**
149          * @return App id in string format. Note that Android will not allow use specific app id.
150          * This must be a category/capability identifier.
151          */
getAppId()152         public @NonNull String getAppId() {
153             return mAppId;
154         }
155 
156         /**
157          * @return The differentiator when multiple traffic descriptor has the same OS and app id.
158          * Must be greater than 1.
159          */
getDifferentiator()160         public int getDifferentiator() {
161             return mDifferentiator;
162         }
163 
164         /**
165          * @return OS/App id in raw byte format.
166          */
getBytes()167         public @NonNull byte[] getBytes() {
168             byte[] osAppId = (mAppId + (mDifferentiator > 1 ? mDifferentiator : "")).getBytes();
169             // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId
170             ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length);
171             bb.putLong(mOsId.getMostSignificantBits());
172             bb.putLong(mOsId.getLeastSignificantBits());
173             bb.put((byte) osAppId.length);
174             bb.put(osAppId);
175             return bb.array();
176         }
177 
178         @Override
toString()179         public String toString() {
180             return "[OsAppId: OS=" + mOsId + ", App=" + mAppId + ", differentiator="
181                     + mDifferentiator + ", raw="
182                     + new BigInteger(1, getBytes()).toString(16) + "]";
183         }
184 
185         @Override
equals(Object o)186         public boolean equals(Object o) {
187             if (this == o) return true;
188             if (o == null || getClass() != o.getClass()) return false;
189             OsAppId osAppId = (OsAppId) o;
190             return mDifferentiator == osAppId.mDifferentiator && mOsId.equals(osAppId.mOsId)
191                     && mAppId.equals(osAppId.mAppId);
192         }
193 
194         @Override
hashCode()195         public int hashCode() {
196             return Objects.hash(mOsId, mAppId, mDifferentiator);
197         }
198     }
199 
200     private final String mDnn;
201     private final OsAppId mOsAppId;
202 
TrafficDescriptor(@onNull Parcel in)203     private TrafficDescriptor(@NonNull Parcel in) {
204         mDnn = in.readString();
205         byte[] osAppIdBytes = in.createByteArray();
206         OsAppId osAppId = null;
207         if (osAppIdBytes != null) {
208             osAppId = new OsAppId(osAppIdBytes);
209         }
210         mOsAppId = osAppId;
211 
212         enforceAllowedIds();
213     }
214 
215     /**
216      * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2
217      * @param dnn optional DNN, which must be used for traffic matching, if present
218      * @param osAppIdRawBytes Raw bytes of OsId + osAppId of the traffic descriptor
219      *
220      * @hide
221      */
TrafficDescriptor(String dnn, @Nullable byte[] osAppIdRawBytes)222     public TrafficDescriptor(String dnn, @Nullable byte[] osAppIdRawBytes) {
223         mDnn = dnn;
224         OsAppId osAppId = null;
225         if (osAppIdRawBytes != null) {
226             osAppId = new OsAppId(osAppIdRawBytes);
227         }
228         mOsAppId = osAppId;
229 
230         enforceAllowedIds();
231     }
232 
233     /**
234      * Enforce the OS id and app id are in the allowed list.
235      *
236      * @throws IllegalArgumentException if ids are not allowed.
237      */
enforceAllowedIds()238     private void enforceAllowedIds() {
239         if (mOsAppId != null && !mOsAppId.getOsId().equals(OsAppId.ANDROID_OS_ID)) {
240             throw new IllegalArgumentException("OS id " + mOsAppId.getOsId() + " does not match "
241                     + OsAppId.ANDROID_OS_ID);
242         }
243 
244         if (mOsAppId != null && !OsAppId.ALLOWED_APP_IDS.contains(mOsAppId.getAppId())) {
245             throw new IllegalArgumentException("Illegal app id " + mOsAppId.getAppId()
246                     + ". Only allowing one of the following " + OsAppId.ALLOWED_APP_IDS);
247         }
248     }
249 
250     /**
251      * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003.
252      * @return the DNN of this traffic descriptor if one is included by the network, null
253      * otherwise.
254      */
getDataNetworkName()255     public @Nullable String getDataNetworkName() {
256         return mDnn;
257     }
258 
259     /**
260      * OsAppId identifies a broader traffic category. Although it names Os/App id, it only includes
261      * OS version with a general/broader category id used as app id.
262      *
263      * @return The id in byte format. {@code null} if not available.
264      */
getOsAppId()265     public @Nullable byte[] getOsAppId() {
266         return mOsAppId != null ? mOsAppId.getBytes() : null;
267     }
268 
269     @Override
describeContents()270     public int describeContents() {
271         return 0;
272     }
273 
274     @NonNull @Override
toString()275     public String toString() {
276         return "TrafficDescriptor={mDnn=" + mDnn + ", " + mOsAppId + "}";
277     }
278 
279     @Override
writeToParcel(@onNull Parcel dest, int flags)280     public void writeToParcel(@NonNull Parcel dest, int flags) {
281         dest.writeString(mDnn);
282         dest.writeByteArray(mOsAppId != null ? mOsAppId.getBytes() : null);
283     }
284 
285     public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR =
286             new Parcelable.Creator<TrafficDescriptor>() {
287                 @Override
288                 public @NonNull TrafficDescriptor createFromParcel(@NonNull Parcel source) {
289                     return new TrafficDescriptor(source);
290                 }
291 
292                 @Override
293                 public @NonNull TrafficDescriptor[] newArray(int size) {
294                     return new TrafficDescriptor[size];
295                 }
296             };
297 
298     @Override
equals(@ullable Object o)299     public boolean equals(@Nullable Object o) {
300         if (this == o) return true;
301         if (o == null || getClass() != o.getClass()) return false;
302         TrafficDescriptor that = (TrafficDescriptor) o;
303         return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId);
304     }
305 
306     @Override
hashCode()307     public int hashCode() {
308         return Objects.hash(mDnn, mOsAppId);
309     }
310 
311     /**
312      * Provides a convenient way to set the fields of a {@link TrafficDescriptor} when creating a
313      * new instance.
314      *
315      * <p>The example below shows how you might create a new {@code TrafficDescriptor}:
316      *
317      * <pre><code>
318      *
319      * TrafficDescriptor response = new TrafficDescriptor.Builder()
320      *     .setDnn("")
321      *     .build();
322      * </code></pre>
323      *
324      */
325     public static final class Builder {
326         private String mDnn = null;
327         private byte[] mOsAppId = null;
328 
329         /**
330          * Default constructor for Builder.
331          */
Builder()332         public Builder() {
333         }
334 
335         /**
336          * Set the Data Network Name(DNN).
337          *
338          * @return The same instance of the builder.
339          */
340         @NonNull
setDataNetworkName(@onNull String dnn)341         public Builder setDataNetworkName(@NonNull String dnn) {
342             this.mDnn = dnn;
343             return this;
344         }
345 
346         /**
347          * Set the OS App ID (including OS Id as defined in the specs).
348          *
349          * @return The same instance of the builder.
350          */
351         @NonNull
setOsAppId(@onNull byte[] osAppId)352         public Builder setOsAppId(@NonNull byte[] osAppId) {
353             this.mOsAppId = osAppId;
354             return this;
355         }
356 
357         /**
358          * Build the {@link TrafficDescriptor}.
359          *
360          * @throws IllegalArgumentException if DNN and OS App ID are null.
361          *
362          * @return the {@link TrafficDescriptor} object.
363          */
364         @NonNull
build()365         public TrafficDescriptor build() {
366             if (this.mDnn == null && this.mOsAppId == null) {
367                 throw new IllegalArgumentException("DNN and OS App ID are null");
368             }
369             return new TrafficDescriptor(this.mDnn, this.mOsAppId);
370         }
371     }
372 }
373