OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / packages / SettingsLib / src / com / android / settingslib / net / DataUsageController.java
1 /*
2  * Copyright (C) 2015 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.settingslib.net;
18
19 import android.content.Context;
20 import android.net.ConnectivityManager;
21 import android.net.INetworkStatsService;
22 import android.net.INetworkStatsSession;
23 import android.net.NetworkPolicy;
24 import android.net.NetworkPolicyManager;
25 import android.net.NetworkStatsHistory;
26 import android.net.NetworkTemplate;
27 import android.os.RemoteException;
28 import android.os.ServiceManager;
29 import android.telephony.SubscriptionManager;
30 import android.telephony.TelephonyManager;
31 import android.text.format.DateUtils;
32 import android.text.format.Time;
33 import android.util.Log;
34
35 import com.android.settingslib.R;
36
37 import java.util.Date;
38 import java.util.Locale;
39
40 import static android.net.ConnectivityManager.TYPE_MOBILE;
41 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
42 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
43 import static android.telephony.TelephonyManager.SIM_STATE_READY;
44 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
45 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
46 import static android.net.TrafficStats.MB_IN_BYTES;
47
48 public class DataUsageController {
49
50     private static final String TAG = "DataUsageController";
51     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
52     private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
53     private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
54     private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
55             PERIOD_BUILDER, Locale.getDefault());
56
57     private final Context mContext;
58     private final TelephonyManager mTelephonyManager;
59     private final ConnectivityManager mConnectivityManager;
60     private final INetworkStatsService mStatsService;
61     private final NetworkPolicyManager mPolicyManager;
62
63     private INetworkStatsSession mSession;
64     private Callback mCallback;
65     private NetworkNameProvider mNetworkController;
66
67     public DataUsageController(Context context) {
68         mContext = context;
69         mTelephonyManager = TelephonyManager.from(context);
70         mConnectivityManager = ConnectivityManager.from(context);
71         mStatsService = INetworkStatsService.Stub.asInterface(
72                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
73         mPolicyManager = NetworkPolicyManager.from(mContext);
74     }
75
76     public void setNetworkController(NetworkNameProvider networkController) {
77         mNetworkController = networkController;
78     }
79
80     /**
81      * Returns the default warning level in bytes.
82      */
83     public long getDefaultWarningLevel() {
84         return MB_IN_BYTES
85                 * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
86     }
87
88     private INetworkStatsSession getSession() {
89         if (mSession == null) {
90             try {
91                 mSession = mStatsService.openSession();
92             } catch (RemoteException e) {
93                 Log.w(TAG, "Failed to open stats session", e);
94             } catch (RuntimeException e) {
95                 Log.w(TAG, "Failed to open stats session", e);
96             }
97         }
98         return mSession;
99     }
100
101     public void setCallback(Callback callback) {
102         mCallback = callback;
103     }
104
105     private DataUsageInfo warn(String msg) {
106         Log.w(TAG, "Failed to get data usage, " + msg);
107         return null;
108     }
109
110     private static Time addMonth(Time t, int months) {
111         final Time rt = new Time(t);
112         rt.set(t.monthDay, t.month + months, t.year);
113         rt.normalize(false);
114         return rt;
115     }
116
117     public DataUsageInfo getDataUsageInfo() {
118         final String subscriberId = getActiveSubscriberId(mContext);
119         if (subscriberId == null) {
120             return warn("no subscriber id");
121         }
122         NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
123         template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds());
124
125         return getDataUsageInfo(template);
126     }
127
128     public DataUsageInfo getWifiDataUsageInfo() {
129         NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
130         return getDataUsageInfo(template);
131     }
132
133     public DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
134         final INetworkStatsSession session = getSession();
135         if (session == null) {
136             return warn("no stats session");
137         }
138         final NetworkPolicy policy = findNetworkPolicy(template);
139         try {
140             final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
141             final long now = System.currentTimeMillis();
142             final long start, end;
143             if (policy != null && policy.cycleDay > 0) {
144                 // period = determined from cycleDay
145                 if (DEBUG) Log.d(TAG, "Cycle day=" + policy.cycleDay + " tz="
146                         + policy.cycleTimezone);
147                 final Time nowTime = new Time(policy.cycleTimezone);
148                 nowTime.setToNow();
149                 final Time policyTime = new Time(nowTime);
150                 policyTime.set(policy.cycleDay, policyTime.month, policyTime.year);
151                 policyTime.normalize(false);
152                 if (nowTime.after(policyTime)) {
153                     start = policyTime.toMillis(false);
154                     end = addMonth(policyTime, 1).toMillis(false);
155                 } else {
156                     start = addMonth(policyTime, -1).toMillis(false);
157                     end = policyTime.toMillis(false);
158                 }
159             } else {
160                 // period = last 4 wks
161                 end = now;
162                 start = now - DateUtils.WEEK_IN_MILLIS * 4;
163             }
164             final long callStart = System.currentTimeMillis();
165             final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
166             final long callEnd = System.currentTimeMillis();
167             if (DEBUG) Log.d(TAG, String.format("history call from %s to %s now=%s took %sms: %s",
168                     new Date(start), new Date(end), new Date(now), callEnd - callStart,
169                     historyEntryToString(entry)));
170             if (entry == null) {
171                 return warn("no entry data");
172             }
173             final long totalBytes = entry.rxBytes + entry.txBytes;
174             final DataUsageInfo usage = new DataUsageInfo();
175             usage.startDate = start;
176             usage.usageLevel = totalBytes;
177             usage.period = formatDateRange(start, end);
178             if (policy != null) {
179                 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
180                 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
181             } else {
182                 usage.warningLevel = getDefaultWarningLevel();
183             }
184             if (usage != null && mNetworkController != null) {
185                 usage.carrier = mNetworkController.getMobileDataNetworkName();
186             }
187             return usage;
188         } catch (RemoteException e) {
189             return warn("remote call failed");
190         }
191     }
192
193     private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
194         if (mPolicyManager == null || template == null) return null;
195         final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
196         if (policies == null) return null;
197         final int N = policies.length;
198         for (int i = 0; i < N; i++) {
199             final NetworkPolicy policy = policies[i];
200             if (policy != null && template.equals(policy.template)) {
201                 return policy;
202             }
203         }
204         return null;
205     }
206
207     private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
208         return entry == null ? null : new StringBuilder("Entry[")
209                 .append("bucketDuration=").append(entry.bucketDuration)
210                 .append(",bucketStart=").append(entry.bucketStart)
211                 .append(",activeTime=").append(entry.activeTime)
212                 .append(",rxBytes=").append(entry.rxBytes)
213                 .append(",rxPackets=").append(entry.rxPackets)
214                 .append(",txBytes=").append(entry.txBytes)
215                 .append(",txPackets=").append(entry.txPackets)
216                 .append(",operations=").append(entry.operations)
217                 .append(']').toString();
218     }
219
220     public void setMobileDataEnabled(boolean enabled) {
221         Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled);
222         mTelephonyManager.setDataEnabled(enabled);
223         if (mCallback != null) {
224             mCallback.onMobileDataEnabled(enabled);
225         }
226     }
227
228     public boolean isMobileDataSupported() {
229         // require both supported network and ready SIM
230         return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
231                 && mTelephonyManager.getSimState() == SIM_STATE_READY;
232     }
233
234     public boolean isMobileDataEnabled() {
235         return mTelephonyManager.getDataEnabled();
236     }
237
238     private static String getActiveSubscriberId(Context context) {
239         final TelephonyManager tele = TelephonyManager.from(context);
240         final String actualSubscriberId = tele.getSubscriberId(
241                 SubscriptionManager.getDefaultDataSubscriptionId());
242         return actualSubscriberId;
243     }
244
245     private String formatDateRange(long start, long end) {
246         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
247         synchronized (PERIOD_BUILDER) {
248             PERIOD_BUILDER.setLength(0);
249             return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null)
250                     .toString();
251         }
252     }
253
254     public interface NetworkNameProvider {
255         String getMobileDataNetworkName();
256     }
257
258     public static class DataUsageInfo {
259         public String carrier;
260         public String period;
261         public long startDate;
262         public long limitLevel;
263         public long warningLevel;
264         public long usageLevel;
265     }
266
267     public interface Callback {
268         void onMobileDataEnabled(boolean enabled);
269     }
270 }