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