1 /*
2  * Copyright (C) 2019 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.car.settings.datausage;
18 
19 import android.content.Context;
20 import android.net.NetworkStats;
21 import android.net.NetworkTemplate;
22 import android.telephony.SubscriptionInfo;
23 import android.telephony.SubscriptionManager;
24 import android.telephony.SubscriptionPlan;
25 import android.telephony.TelephonyManager;
26 import android.text.BidiFormatter;
27 import android.text.format.DateUtils;
28 import android.text.format.Formatter;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.Nullable;
32 import androidx.annotation.VisibleForTesting;
33 
34 import com.android.car.settings.R;
35 import com.android.internal.util.CollectionUtils;
36 
37 import java.util.Calendar;
38 import java.util.List;
39 import java.util.Set;
40 
41 /** Provides helpful utilities related to data usage. */
42 public final class DataUsageUtils {
43 
44     @VisibleForTesting
45     static final long PETA = 1000000000000000L;
46 
DataUsageUtils()47     private DataUsageUtils() {
48     }
49 
50     /**
51      * Returns the mobile network template given the subscription id.
52      */
getMobileNetworkTemplate(TelephonyManager telephonyManager, int subscriptionId)53     public static NetworkTemplate getMobileNetworkTemplate(TelephonyManager telephonyManager,
54             int subscriptionId) {
55         String subscriberId = telephonyManager.getSubscriberId(subscriptionId);
56         NetworkTemplate.Builder builder =
57                 new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
58                 .setMeteredness(NetworkStats.METERED_YES);
59         if (subscriberId != null) {
60             builder.setSubscriberIds(Set.of(subscriberId));
61         }
62         return normalizeMobileTemplate(builder.build(), telephonyManager.getMergedSubscriberIds());
63     }
64 
normalizeMobileTemplate( @onNull NetworkTemplate template, @Nullable String[] mergedSet)65     private static NetworkTemplate normalizeMobileTemplate(
66             @NonNull NetworkTemplate template, @Nullable String[] mergedSet) {
67         if (template.getSubscriberIds().isEmpty() || mergedSet == null) return template;
68         // The input template should have at most 1 subscriberId.
69         String subscriberId = template.getSubscriberIds().iterator().next();
70 
71         if (Set.of(mergedSet).contains(subscriberId)) {
72             // Requested template subscriber is part of the merge group; return
73             // a template that matches all merged subscribers.
74             return new NetworkTemplate.Builder(template.getMatchRule())
75                     .setSubscriberIds(Set.of(mergedSet))
76                     .setMeteredness(template.getMeteredness()).build();
77         }
78 
79         return template;
80     }
81 
82     /**
83      * Returns the default subscription if available else returns
84      * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
85      */
getDefaultSubscriptionId(SubscriptionManager subscriptionManager)86     public static int getDefaultSubscriptionId(SubscriptionManager subscriptionManager) {
87         if (subscriptionManager == null) {
88             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
89         }
90         SubscriptionInfo subscriptionInfo = subscriptionManager.getDefaultDataSubscriptionInfo();
91         if (subscriptionInfo == null) {
92             List<SubscriptionInfo> list = subscriptionManager.getAllSubscriptionInfoList();
93             if (list.size() == 0) {
94                 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
95             }
96             subscriptionInfo = list.get(0);
97         }
98         return subscriptionInfo.getSubscriptionId();
99     }
100 
101     /**
102      * Format byte value to readable string using IEC units.
103      */
bytesToIecUnits(Context context, long byteValue)104     public static CharSequence bytesToIecUnits(Context context, long byteValue) {
105         Formatter.BytesResult res = Formatter.formatBytes(context.getResources(), byteValue,
106                 Formatter.FLAG_IEC_UNITS);
107         return BidiFormatter.getInstance().unicodeWrap(context.getString(
108                 com.android.internal.R.string.fileSizeSuffix, res.value, res.units));
109     }
110 
111     /**
112      * Generate a string that lists byte value to readable string using IEC units, and date range
113      * from specified start date to current date.
114      */
formatDataUsageInCycle(Context context, long byteValue, long fromTimeInMillis)115     public static CharSequence formatDataUsageInCycle(Context context, long byteValue,
116             long fromTimeInMillis) {
117         CharSequence usage = bytesToIecUnits(context, byteValue);
118         int flags = DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_NO_YEAR;
119         String fromText = DateUtils.formatDateTime(context, fromTimeInMillis, flags);
120         String toText = DateUtils.formatDateTime(context, Calendar.getInstance().getTimeInMillis(),
121                 flags);
122         return context.getString(R.string.network_and_internet_data_usage_time_range_summary, usage,
123                 fromText, toText);
124     }
125 
126     /**
127      * Returns the primary subscription plan. Returns {@code null} if {@link SubscriptionPlan}
128      * doesn't exist for a given subscriptionId or if the first {@link SubscriptionPlan} has
129      * invalid properties.
130      */
131     @Nullable
getPrimaryPlan(SubscriptionManager subManager, int subscriptionId)132     public static SubscriptionPlan getPrimaryPlan(SubscriptionManager subManager,
133             int subscriptionId) {
134         List<SubscriptionPlan> plans = subManager.getSubscriptionPlans(subscriptionId);
135         if (CollectionUtils.isEmpty(plans)) {
136             return null;
137         }
138         // First plan in the list is the primary plan
139         SubscriptionPlan plan = plans.get(0);
140         return plan.getDataLimitBytes() > 0
141                 && saneSize(plan.getDataUsageBytes())
142                 && plan.getCycleRule() != null ? plan : null;
143     }
144 
saneSize(long value)145     private static boolean saneSize(long value) {
146         return value >= 0L && value < PETA;
147     }
148 }
149