[go: nahoru, domu]

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