• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 com.android.networkstack.tethering.metrics;
18 
19 import static android.app.usage.NetworkStats.Bucket.STATE_ALL;
20 import static android.app.usage.NetworkStats.Bucket.TAG_NONE;
21 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
22 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
23 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
24 import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
25 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
26 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
27 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
28 import static android.net.NetworkStats.METERED_YES;
29 import static android.net.NetworkStats.UID_TETHERING;
30 import static android.net.NetworkTemplate.MATCH_BLUETOOTH;
31 import static android.net.NetworkTemplate.MATCH_ETHERNET;
32 import static android.net.NetworkTemplate.MATCH_MOBILE;
33 import static android.net.NetworkTemplate.MATCH_WIFI;
34 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
35 import static android.net.TetheringManager.TETHERING_ETHERNET;
36 import static android.net.TetheringManager.TETHERING_NCM;
37 import static android.net.TetheringManager.TETHERING_USB;
38 import static android.net.TetheringManager.TETHERING_WIFI;
39 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
40 import static android.net.TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
41 import static android.net.TetheringManager.TETHER_ERROR_DISABLE_FORWARDING_ERROR;
42 import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
43 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
44 import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
45 import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
46 import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
47 import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
48 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
49 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
50 import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
51 import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
52 import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
53 import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
54 import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
55 import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
56 
57 import android.annotation.Nullable;
58 import android.app.usage.NetworkStats;
59 import android.app.usage.NetworkStatsManager;
60 import android.content.Context;
61 import android.net.NetworkCapabilities;
62 import android.net.NetworkTemplate;
63 import android.os.Handler;
64 import android.os.HandlerThread;
65 import android.stats.connectivity.DownstreamType;
66 import android.stats.connectivity.ErrorCode;
67 import android.stats.connectivity.UpstreamType;
68 import android.stats.connectivity.UserType;
69 import android.util.ArrayMap;
70 import android.util.Log;
71 import android.util.SparseArray;
72 
73 import androidx.annotation.NonNull;
74 import androidx.annotation.VisibleForTesting;
75 
76 import com.android.modules.utils.build.SdkLevel;
77 import com.android.net.module.util.DeviceConfigUtils;
78 import com.android.net.module.util.HandlerUtils;
79 import com.android.networkstack.tethering.UpstreamNetworkState;
80 
81 import java.util.ArrayList;
82 import java.util.Arrays;
83 
84 /**
85  * Collection of utilities for tethering metrics.
86  *
87  *  <p>This class is thread-safe. All accesses to this class will be either posting to the internal
88  *  handler thread for processing or checking whether the access is from the internal handler
89  *  thread. However, the constructor is an exception, as it is called on another thread.
90  *
91  * To see if the logs are properly sent to statsd, execute following commands
92  *
93  * $ adb shell cmd stats print-logs
94  * $ adb logcat | grep statsd OR $ adb logcat -b stats
95  *
96  * @hide
97  */
98 public class TetheringMetrics {
99     private static final String TAG = TetheringMetrics.class.getSimpleName();
100     private static final boolean DBG = false;
101     private static final String SETTINGS_PKG_NAME = "com.android.settings";
102     private static final String SYSTEMUI_PKG_NAME = "com.android.systemui";
103     private static final String GMS_PKG_NAME = "com.google.android.gms";
104     /**
105      * A feature flag to control whether upstream data usage metrics should be enabled.
106      */
107     private static final String TETHER_UPSTREAM_DATA_USAGE_METRICS =
108             "tether_upstream_data_usage_metrics";
109     @VisibleForTesting
110     static final DataUsage EMPTY = new DataUsage(0L /* txBytes */, 0L /* rxBytes */);
111     private final SparseArray<NetworkTetheringReported.Builder> mBuilderMap = new SparseArray<>();
112     private final SparseArray<Long> mDownstreamStartTime = new SparseArray<Long>();
113     private final ArrayList<RecordUpstreamEvent> mUpstreamEventList = new ArrayList<>();
114     // Store the last reported data usage for each upstream type to be used for calculating the
115     // usage delta. The keys are the upstream types, and the values are the tethering UID data
116     // usage for the corresponding types. Retrieve the baseline data usage when tethering is
117     // enabled, update it when the upstream changes, and clear it when tethering is disabled.
118     private final ArrayMap<UpstreamType, DataUsage> mLastReportedUpstreamUsage = new ArrayMap<>();
119     private final Context mContext;
120     private final Dependencies mDependencies;
121     private final NetworkStatsManager mNetworkStatsManager;
122     private final Handler mHandler;
123     private UpstreamType mCurrentUpstream = null;
124     private Long mCurrentUpStreamStartTime = 0L;
125 
126     /**
127      * Dependencies of TetheringMetrics, for injection in tests.
128      */
129     @VisibleForTesting
130     public static class Dependencies {
131         /**
132          * @see TetheringStatsLog
133          */
write(NetworkTetheringReported reported)134         public void write(NetworkTetheringReported reported) {
135             TetheringStatsLog.write(
136                     TetheringStatsLog.NETWORK_TETHERING_REPORTED,
137                     reported.getErrorCode().getNumber(),
138                     reported.getDownstreamType().getNumber(),
139                     reported.getUpstreamType().getNumber(),
140                     reported.getUserType().getNumber(),
141                     reported.getUpstreamEvents().toByteArray(),
142                     reported.getDurationMillis());
143         }
144 
145         /**
146          * @see System#currentTimeMillis()
147          */
timeNow()148         public long timeNow() {
149             return System.currentTimeMillis();
150         }
151 
152         /**
153          * Indicates whether {@link #TETHER_UPSTREAM_DATA_USAGE_METRICS} is enabled.
154          */
isUpstreamDataUsageMetricsEnabled(Context context)155         public boolean isUpstreamDataUsageMetricsEnabled(Context context) {
156             // Getting data usage requires building a NetworkTemplate. However, the
157             // NetworkTemplate#Builder API was introduced in Android T.
158             return SdkLevel.isAtLeastT() && DeviceConfigUtils.isTetheringFeatureNotChickenedOut(
159                     context, TETHER_UPSTREAM_DATA_USAGE_METRICS);
160         }
161 
162         /**
163          * @see Handler
164          *
165          * Note: This should only be called once, within the constructor, as it creates a new
166          * thread. Calling it multiple times could lead to a thread leak.
167          */
168         @NonNull
createHandler()169         public Handler createHandler() {
170             final HandlerThread thread = new HandlerThread(TAG);
171             thread.start();
172             return new Handler(thread.getLooper());
173         }
174     }
175 
176     /**
177      * Constructor for the TetheringMetrics class.
178      *
179      * @param context The Context object used to access system services.
180      */
TetheringMetrics(Context context)181     public TetheringMetrics(Context context) {
182         this(context, new Dependencies());
183     }
184 
TetheringMetrics(Context context, Dependencies dependencies)185     TetheringMetrics(Context context, Dependencies dependencies) {
186         mContext = context;
187         mDependencies = dependencies;
188         mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
189         mHandler = dependencies.createHandler();
190     }
191 
192     @VisibleForTesting
193     static class DataUsage {
194         public final long txBytes;
195         public final long rxBytes;
196 
DataUsage(long txBytes, long rxBytes)197         DataUsage(long txBytes, long rxBytes) {
198             this.txBytes = txBytes;
199             this.rxBytes = rxBytes;
200         }
201 
202         /*** Calculate the data usage delta from give new and old usage */
subtract(DataUsage newUsage, DataUsage oldUsage)203         public static DataUsage subtract(DataUsage newUsage, DataUsage oldUsage) {
204             return new DataUsage(
205                     newUsage.txBytes - oldUsage.txBytes,
206                     newUsage.rxBytes - oldUsage.rxBytes);
207         }
208 
209         @Override
hashCode()210         public int hashCode() {
211             return (int) (txBytes & 0xFFFFFFFF)
212                     + ((int) (txBytes >> 32) * 3)
213                     + ((int) (rxBytes & 0xFFFFFFFF) * 5)
214                     + ((int) (rxBytes >> 32) * 7);
215         }
216 
217         @Override
equals(Object other)218         public boolean equals(Object other) {
219             if (this == other) {
220                 return true;
221             }
222             if (!(other instanceof DataUsage)) {
223                 return false;
224             }
225             return txBytes == ((DataUsage) other).txBytes
226                     && rxBytes == ((DataUsage) other).rxBytes;
227         }
228 
229     }
230 
231     private static class RecordUpstreamEvent {
232         final long mStartTime;
233         final long mStopTime;
234         final UpstreamType mUpstreamType;
235         final DataUsage mDataUsage;
236 
RecordUpstreamEvent(final long startTime, final long stopTime, final UpstreamType upstream, final DataUsage dataUsage)237         RecordUpstreamEvent(final long startTime, final long stopTime,
238                 final UpstreamType upstream, final DataUsage dataUsage) {
239             mStartTime = startTime;
240             mStopTime = stopTime;
241             mUpstreamType = upstream;
242             mDataUsage = dataUsage;
243         }
244     }
245 
246     /**
247      * Creates a |NetworkTetheringReported.Builder| object to update the tethering stats for the
248      * specified downstream type and caller's package name. Initializes the upstream events, error
249      * code, and duration to default values. Sets the start time for the downstream type in the
250      * |mDownstreamStartTime| map.
251      * @param downstreamType The type of downstream connection (e.g. Wifi, USB, Bluetooth).
252      * @param callerPkg The package name of the caller.
253      */
createBuilder(final int downstreamType, final String callerPkg)254     public void createBuilder(final int downstreamType, final String callerPkg) {
255         mHandler.post(() -> handleCreateBuilder(downstreamType, callerPkg));
256     }
257 
handleCreateBuilder(final int downstreamType, final String callerPkg)258     private void handleCreateBuilder(final int downstreamType, final String callerPkg) {
259         NetworkTetheringReported.Builder statsBuilder = NetworkTetheringReported.newBuilder()
260                 .setDownstreamType(downstreamTypeToEnum(downstreamType))
261                 .setUserType(userTypeToEnum(callerPkg))
262                 .setUpstreamType(UpstreamType.UT_UNKNOWN)
263                 .setErrorCode(ErrorCode.EC_NO_ERROR)
264                 .setUpstreamEvents(UpstreamEvents.newBuilder())
265                 .setDurationMillis(0);
266         mBuilderMap.put(downstreamType, statsBuilder);
267         mDownstreamStartTime.put(downstreamType, mDependencies.timeNow());
268     }
269 
270     /**
271      * Update the error code of the given downstream type in the Tethering stats.
272      * @param downstreamType The downstream type whose error code to update.
273      * @param errCode The error code to set.
274      */
updateErrorCode(final int downstreamType, final int errCode)275     public void updateErrorCode(final int downstreamType, final int errCode) {
276         mHandler.post(() -> handleUpdateErrorCode(downstreamType, errCode));
277     }
278 
handleUpdateErrorCode(final int downstreamType, final int errCode)279     private void handleUpdateErrorCode(final int downstreamType, final int errCode) {
280         NetworkTetheringReported.Builder statsBuilder = mBuilderMap.get(downstreamType);
281         if (statsBuilder == null) {
282             Log.e(TAG, "Given downstreamType does not exist, this is a bug!");
283             return;
284         }
285         statsBuilder.setErrorCode(errorCodeToEnum(errCode));
286     }
287 
288     /**
289      * Calculates the data usage difference between the current and previous usage for the
290      * specified upstream type.
291      *
292      * Note: This must be called before updating mCurrentUpstream when changing the upstream.
293      *
294      * @return A DataUsage object containing the calculated difference in transmitted (tx) and
295      *         received (rx) bytes.
296      */
calculateDataUsageDelta(@ullable UpstreamType upstream)297     private DataUsage calculateDataUsageDelta(@Nullable UpstreamType upstream) {
298         if (!mDependencies.isUpstreamDataUsageMetricsEnabled(mContext)) {
299             return EMPTY;
300         }
301 
302         if (upstream == null || !isUsageSupportedForUpstreamType(upstream)) {
303             return EMPTY;
304         }
305 
306         final DataUsage oldUsage = mLastReportedUpstreamUsage.getOrDefault(upstream, EMPTY);
307         if (oldUsage.equals(EMPTY)) {
308             Log.d(TAG, "No usage baseline for the upstream=" + upstream);
309             return EMPTY;
310         }
311         // TODO(b/370724247): Fix data usage which might be incorrect if the device uses
312         //  tethering with the same upstream for over 15 days.
313         // Need to refresh the baseline usage data. If the network switches back to Wi-Fi after
314         // using cellular data (Wi-Fi -> Cellular -> Wi-Fi), the old baseline might be
315         // inaccurate, leading to incorrect delta calculations.
316         final DataUsage newUsage = getCurrentDataUsageForUpstreamType(upstream);
317         mLastReportedUpstreamUsage.put(upstream, newUsage);
318         return DataUsage.subtract(newUsage, oldUsage);
319     }
320 
321     /**
322      * Update the list of upstream types and their duration whenever the current upstream type
323      * changes.
324      * @param ns The UpstreamNetworkState object representing the current upstream network state.
325      */
maybeUpdateUpstreamType(@ullable final UpstreamNetworkState ns)326     public void maybeUpdateUpstreamType(@Nullable final UpstreamNetworkState ns) {
327         mHandler.post(() -> handleMaybeUpdateUpstreamType(ns));
328     }
329 
handleMaybeUpdateUpstreamType(@ullable final UpstreamNetworkState ns)330     private void handleMaybeUpdateUpstreamType(@Nullable final UpstreamNetworkState ns) {
331         UpstreamType upstream = transportTypeToUpstreamTypeEnum(ns);
332         if (upstream.equals(mCurrentUpstream)) return;
333 
334         final long newTime = mDependencies.timeNow();
335         if (mCurrentUpstream != null) {
336             final DataUsage dataUsage = calculateDataUsageDelta(mCurrentUpstream);
337             mUpstreamEventList.add(new RecordUpstreamEvent(mCurrentUpStreamStartTime, newTime,
338                     mCurrentUpstream, dataUsage));
339         }
340         mCurrentUpstream = upstream;
341         mCurrentUpStreamStartTime = newTime;
342     }
343 
344     /**
345      * Updates the upstream events builder with a new upstream event.
346      * @param upstreamEventsBuilder the builder for the upstream events list
347      * @param start the start time of the upstream event
348      * @param stop the stop time of the upstream event
349      * @param upstream the type of upstream type (e.g. Wifi, Cellular, Bluetooth, ...)
350      */
addUpstreamEvent(final UpstreamEvents.Builder upstreamEventsBuilder, final long start, final long stop, @Nullable final UpstreamType upstream, final long txBytes, final long rxBytes)351     private void addUpstreamEvent(final UpstreamEvents.Builder upstreamEventsBuilder,
352             final long start, final long stop, @Nullable final UpstreamType upstream,
353             final long txBytes, final long rxBytes) {
354         final UpstreamEvent.Builder upstreamEventBuilder = UpstreamEvent.newBuilder()
355                 .setUpstreamType(upstream == null ? UpstreamType.UT_NO_NETWORK : upstream)
356                 .setDurationMillis(stop - start)
357                 .setTxBytes(txBytes)
358                 .setRxBytes(rxBytes);
359         upstreamEventsBuilder.addUpstreamEvent(upstreamEventBuilder);
360     }
361 
362     /**
363      * Updates the |NetworkTetheringReported.Builder| with relevant upstream events associated with
364      * the downstream event identified by the given downstream start time.
365      *
366      * This method iterates through the list of upstream events and adds any relevant events to a
367      * |UpstreamEvents.Builder|. Upstream events are considered relevant if their stop time is
368      * greater than or equal to the given downstream start time. The method also adds the last
369      * upstream event that occurred up until the current time.
370      *
371      * The resulting |UpstreamEvents.Builder| is then added to the
372      * |NetworkTetheringReported.Builder|, along with the duration of the downstream event
373      * (i.e., stop time minus downstream start time).
374      *
375      * @param statsBuilder the builder for the NetworkTetheringReported message
376      * @param downstreamStartTime the start time of the downstream event to find relevant upstream
377      * events for
378      */
noteDownstreamStopped(final NetworkTetheringReported.Builder statsBuilder, final long downstreamStartTime)379     private void noteDownstreamStopped(final NetworkTetheringReported.Builder statsBuilder,
380                     final long downstreamStartTime) {
381         UpstreamEvents.Builder upstreamEventsBuilder = UpstreamEvents.newBuilder();
382 
383         for (RecordUpstreamEvent event : mUpstreamEventList) {
384             if (downstreamStartTime > event.mStopTime) continue;
385 
386             final long startTime = Math.max(downstreamStartTime, event.mStartTime);
387             // Handle completed upstream events.
388             addUpstreamEvent(upstreamEventsBuilder, startTime, event.mStopTime,
389                     event.mUpstreamType, event.mDataUsage.txBytes, event.mDataUsage.rxBytes);
390         }
391         final long startTime = Math.max(downstreamStartTime, mCurrentUpStreamStartTime);
392         final long stopTime = mDependencies.timeNow();
393         // Handle the last upstream event.
394         final DataUsage dataUsage = calculateDataUsageDelta(mCurrentUpstream);
395         addUpstreamEvent(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream,
396                 dataUsage.txBytes, dataUsage.rxBytes);
397         statsBuilder.setUpstreamEvents(upstreamEventsBuilder);
398         statsBuilder.setDurationMillis(stopTime - downstreamStartTime);
399     }
400 
401     /**
402      * Removes tethering statistics for the given downstream type. If there are any stats to write
403      * for the downstream event associated with the type, they are written before removing the
404      * statistics.
405      *
406      * If the given downstream type does not exist in the map, an error message is logged and the
407      * method returns without doing anything.
408      *
409      * @param downstreamType the type of downstream event to remove statistics for
410      */
sendReport(final int downstreamType)411     public void sendReport(final int downstreamType) {
412         mHandler.post(() -> handleSendReport(downstreamType));
413     }
414 
handleSendReport(final int downstreamType)415     private void handleSendReport(final int downstreamType) {
416         final NetworkTetheringReported.Builder statsBuilder = mBuilderMap.get(downstreamType);
417         if (statsBuilder == null) {
418             Log.e(TAG, "Given downstreamType does not exist, this is a bug!");
419             return;
420         }
421 
422         noteDownstreamStopped(statsBuilder, mDownstreamStartTime.get(downstreamType));
423         write(statsBuilder.build());
424 
425         mBuilderMap.remove(downstreamType);
426         mDownstreamStartTime.remove(downstreamType);
427     }
428 
429     /**
430      * Collects tethering statistics and writes them to the statsd pipeline. This method takes in a
431      * NetworkTetheringReported object, extracts its fields and uses them to write statistics data
432      * to the statsd pipeline.
433      *
434      * @param reported a NetworkTetheringReported object containing statistics to write
435      */
write(@onNull final NetworkTetheringReported reported)436     private void write(@NonNull final NetworkTetheringReported reported) {
437         mDependencies.write(reported);
438         if (DBG) {
439             Log.d(
440                     TAG,
441                     "Write errorCode: "
442                     + reported.getErrorCode().getNumber()
443                     + ", downstreamType: "
444                     + reported.getDownstreamType().getNumber()
445                     + ", upstreamType: "
446                     + reported.getUpstreamType().getNumber()
447                     + ", userType: "
448                     + reported.getUserType().getNumber()
449                     + ", upstreamTypes: "
450                     + Arrays.toString(reported.getUpstreamEvents().toByteArray())
451                     + ", durationMillis: "
452                     + reported.getDurationMillis());
453         }
454     }
455 
456     /**
457      * Initialize the upstream data usage baseline when tethering is turned on.
458      */
initUpstreamUsageBaseline()459     public void initUpstreamUsageBaseline() {
460         mHandler.post(() -> handleInitUpstreamUsageBaseline());
461     }
462 
handleInitUpstreamUsageBaseline()463     private void handleInitUpstreamUsageBaseline() {
464         if (!mDependencies.isUpstreamDataUsageMetricsEnabled(mContext)) {
465             return;
466         }
467 
468         if (!mLastReportedUpstreamUsage.isEmpty()) {
469             Log.wtf(TAG, "The upstream usage baseline has been initialed.");
470             return;
471         }
472 
473         for (UpstreamType type : UpstreamType.values()) {
474             if (!isUsageSupportedForUpstreamType(type)) continue;
475             mLastReportedUpstreamUsage.put(type, getCurrentDataUsageForUpstreamType(type));
476         }
477     }
478 
479     @VisibleForTesting
480     @NonNull
getLastReportedUsageFromUpstreamType(@onNull UpstreamType type)481     DataUsage getLastReportedUsageFromUpstreamType(@NonNull UpstreamType type) {
482         HandlerUtils.ensureRunningOnHandlerThread(mHandler);
483         return mLastReportedUpstreamUsage.getOrDefault(type, EMPTY);
484     }
485 
486 
487     /**
488      * Get the current usage for given upstream type.
489      */
490     @NonNull
getCurrentDataUsageForUpstreamType(@onNull UpstreamType type)491     private DataUsage getCurrentDataUsageForUpstreamType(@NonNull UpstreamType type) {
492         final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState(
493                 buildNetworkTemplateForUpstreamType(type), Long.MIN_VALUE, Long.MAX_VALUE,
494                 UID_TETHERING, TAG_NONE, STATE_ALL);
495 
496         final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
497         Long totalTxBytes = 0L;
498         Long totalRxBytes = 0L;
499         while (stats.hasNextBucket()) {
500             stats.getNextBucket(bucket);
501             totalTxBytes += bucket.getTxBytes();
502             totalRxBytes += bucket.getRxBytes();
503         }
504         return new DataUsage(totalTxBytes, totalRxBytes);
505     }
506 
507     /**
508      * Cleans up the variables related to upstream events when tethering is turned off.
509      */
cleanup()510     public void cleanup() {
511         mHandler.post(() -> handleCleanup());
512     }
513 
handleCleanup()514     private void handleCleanup() {
515         mUpstreamEventList.clear();
516         mCurrentUpstream = null;
517         mCurrentUpStreamStartTime = 0L;
518         mLastReportedUpstreamUsage.clear();
519     }
520 
downstreamTypeToEnum(final int ifaceType)521     private DownstreamType downstreamTypeToEnum(final int ifaceType) {
522         switch(ifaceType) {
523             case TETHERING_WIFI:
524                 return DownstreamType.DS_TETHERING_WIFI;
525             case TETHERING_WIFI_P2P:
526                 return DownstreamType.DS_TETHERING_WIFI_P2P;
527             case TETHERING_USB:
528                 return DownstreamType.DS_TETHERING_USB;
529             case TETHERING_BLUETOOTH:
530                 return DownstreamType.DS_TETHERING_BLUETOOTH;
531             case TETHERING_NCM:
532                 return DownstreamType.DS_TETHERING_NCM;
533             case TETHERING_ETHERNET:
534                 return DownstreamType.DS_TETHERING_ETHERNET;
535             default:
536                 return DownstreamType.DS_UNSPECIFIED;
537         }
538     }
539 
errorCodeToEnum(final int lastError)540     private ErrorCode errorCodeToEnum(final int lastError) {
541         switch(lastError) {
542             case TETHER_ERROR_NO_ERROR:
543                 return ErrorCode.EC_NO_ERROR;
544             case TETHER_ERROR_UNKNOWN_IFACE:
545                 return ErrorCode.EC_UNKNOWN_IFACE;
546             case TETHER_ERROR_SERVICE_UNAVAIL:
547                 return ErrorCode.EC_SERVICE_UNAVAIL;
548             case TETHER_ERROR_UNSUPPORTED:
549                 return ErrorCode.EC_UNSUPPORTED;
550             case TETHER_ERROR_UNAVAIL_IFACE:
551                 return ErrorCode.EC_UNAVAIL_IFACE;
552             case TETHER_ERROR_INTERNAL_ERROR:
553                 return ErrorCode.EC_INTERNAL_ERROR;
554             case TETHER_ERROR_TETHER_IFACE_ERROR:
555                 return ErrorCode.EC_TETHER_IFACE_ERROR;
556             case TETHER_ERROR_UNTETHER_IFACE_ERROR:
557                 return ErrorCode.EC_UNTETHER_IFACE_ERROR;
558             case TETHER_ERROR_ENABLE_FORWARDING_ERROR:
559                 return ErrorCode.EC_ENABLE_FORWARDING_ERROR;
560             case TETHER_ERROR_DISABLE_FORWARDING_ERROR:
561                 return ErrorCode.EC_DISABLE_FORWARDING_ERROR;
562             case TETHER_ERROR_IFACE_CFG_ERROR:
563                 return ErrorCode.EC_IFACE_CFG_ERROR;
564             case TETHER_ERROR_PROVISIONING_FAILED:
565                 return ErrorCode.EC_PROVISIONING_FAILED;
566             case TETHER_ERROR_DHCPSERVER_ERROR:
567                 return ErrorCode.EC_DHCPSERVER_ERROR;
568             case TETHER_ERROR_ENTITLEMENT_UNKNOWN:
569                 return ErrorCode.EC_ENTITLEMENT_UNKNOWN;
570             case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:
571                 return ErrorCode.EC_NO_CHANGE_TETHERING_PERMISSION;
572             case TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION:
573                 return ErrorCode.EC_NO_ACCESS_TETHERING_PERMISSION;
574             default:
575                 return ErrorCode.EC_UNKNOWN_TYPE;
576         }
577     }
578 
userTypeToEnum(final String callerPkg)579     private UserType userTypeToEnum(final String callerPkg) {
580         if (callerPkg.equals(SETTINGS_PKG_NAME)) {
581             return UserType.USER_SETTINGS;
582         } else if (callerPkg.equals(SYSTEMUI_PKG_NAME)) {
583             return UserType.USER_SYSTEMUI;
584         } else if (callerPkg.equals(GMS_PKG_NAME)) {
585             return UserType.USER_GMS;
586         } else {
587             return UserType.USER_UNKNOWN;
588         }
589     }
590 
transportTypeToUpstreamTypeEnum(final UpstreamNetworkState ns)591     private UpstreamType transportTypeToUpstreamTypeEnum(final UpstreamNetworkState ns) {
592         final NetworkCapabilities nc = (ns != null) ? ns.networkCapabilities : null;
593         if (nc == null) return UpstreamType.UT_NO_NETWORK;
594 
595         final int typeCount = nc.getTransportTypes().length;
596         // It's possible for a VCN network to be mapped to UT_UNKNOWN, as it may consist of both
597         // Wi-Fi and cellular transport.
598         // TODO: It's necessary to define a new upstream type for VCN, which can be identified by
599         // NET_CAPABILITY_NOT_VCN_MANAGED.
600         if (typeCount > 1) return UpstreamType.UT_UNKNOWN;
601 
602         if (nc.hasTransport(TRANSPORT_CELLULAR)) return UpstreamType.UT_CELLULAR;
603         if (nc.hasTransport(TRANSPORT_WIFI)) return UpstreamType.UT_WIFI;
604         if (nc.hasTransport(TRANSPORT_BLUETOOTH)) return UpstreamType.UT_BLUETOOTH;
605         if (nc.hasTransport(TRANSPORT_ETHERNET)) return UpstreamType.UT_ETHERNET;
606         if (nc.hasTransport(TRANSPORT_WIFI_AWARE)) return UpstreamType.UT_WIFI_AWARE;
607         if (nc.hasTransport(TRANSPORT_LOWPAN)) return UpstreamType.UT_LOWPAN;
608 
609         return UpstreamType.UT_UNKNOWN;
610     }
611 
612     /**
613      * Check whether tethering metrics' data usage can be collected for a given upstream type.
614      *
615      * @param type the upstream type
616      */
isUsageSupportedForUpstreamType(@onNull UpstreamType type)617     public static boolean isUsageSupportedForUpstreamType(@NonNull UpstreamType type) {
618         switch(type) {
619             case UT_CELLULAR:
620             case UT_WIFI:
621             case UT_BLUETOOTH:
622             case UT_ETHERNET:
623                 return true;
624             default:
625                 break;
626         }
627         return false;
628     }
629 
630     /**
631      * Build NetworkTemplate for the given upstream type.
632      *
633      * <p> NetworkTemplate.Builder API was introduced in Android T.
634      *
635      * @param type the upstream type
636      * @return A NetworkTemplate object with a corresponding match rule or null if tethering
637      * metrics' data usage cannot be collected for a given upstream type.
638      */
639     @Nullable
buildNetworkTemplateForUpstreamType(@onNull UpstreamType type)640     public static NetworkTemplate buildNetworkTemplateForUpstreamType(@NonNull UpstreamType type) {
641         if (!isUsageSupportedForUpstreamType(type)) return null;
642 
643         switch (type) {
644             case UT_CELLULAR:
645                 // TODO: Handle the DUN connection, which is not a default network.
646                 return new NetworkTemplate.Builder(MATCH_MOBILE)
647                         .setMeteredness(METERED_YES)
648                         .setDefaultNetworkStatus(DEFAULT_NETWORK_YES)
649                         .build();
650             case UT_WIFI:
651                 return new NetworkTemplate.Builder(MATCH_WIFI)
652                         .setMeteredness(METERED_YES)
653                         .setDefaultNetworkStatus(DEFAULT_NETWORK_YES)
654                         .build();
655             case UT_BLUETOOTH:
656                 return new NetworkTemplate.Builder(MATCH_BLUETOOTH)
657                         .setMeteredness(METERED_YES)
658                         .setDefaultNetworkStatus(DEFAULT_NETWORK_YES)
659                         .build();
660             case UT_ETHERNET:
661                 return new NetworkTemplate.Builder(MATCH_ETHERNET)
662                         .setMeteredness(METERED_YES)
663                         .setDefaultNetworkStatus(DEFAULT_NETWORK_YES)
664                         .build();
665             default:
666                 Log.e(TAG, "Unsupported UpstreamType: " + type.name());
667                 break;
668         }
669         return null;
670     }
671 }
672