1/*
2 * Copyright (C) 2013 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.server.connectivity;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.net.ConnectivityManager;
24import android.os.RemoteException;
25import android.telephony.PhoneStateListener;
26import android.telephony.ServiceState;
27import android.telephony.SignalStrength;
28import android.telephony.TelephonyManager;
29import android.util.Log;
30
31import com.android.internal.app.IBatteryStats;
32import com.android.internal.telephony.IccCardConstants;
33import com.android.internal.telephony.TelephonyIntents;
34import com.android.server.am.BatteryStatsService;
35
36public class DataConnectionStats extends BroadcastReceiver {
37    private static final String TAG = "DataConnectionStats";
38    private static final boolean DEBUG = false;
39
40    private final Context mContext;
41    private final IBatteryStats mBatteryStats;
42
43    private IccCardConstants.State mSimState = IccCardConstants.State.READY;
44    private SignalStrength mSignalStrength;
45    private ServiceState mServiceState;
46    private int mDataState = TelephonyManager.DATA_DISCONNECTED;
47
48    public DataConnectionStats(Context context) {
49        mContext = context;
50        mBatteryStats = BatteryStatsService.getService();
51    }
52
53    public void startMonitoring() {
54        TelephonyManager phone =
55                (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
56        phone.listen(mPhoneStateListener,
57                PhoneStateListener.LISTEN_SERVICE_STATE
58              | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
59              | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
60              | PhoneStateListener.LISTEN_DATA_ACTIVITY);
61
62        IntentFilter filter = new IntentFilter();
63        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
64        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
65        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
66        mContext.registerReceiver(this, filter);
67    }
68
69    @Override
70    public void onReceive(Context context, Intent intent) {
71        final String action = intent.getAction();
72        if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
73            updateSimState(intent);
74            notePhoneDataConnectionState();
75        } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
76                action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
77            notePhoneDataConnectionState();
78       }
79    }
80
81    private void notePhoneDataConnectionState() {
82        if (mServiceState == null) {
83            return;
84        }
85        boolean simReadyOrUnknown = mSimState == IccCardConstants.State.READY
86                || mSimState == IccCardConstants.State.UNKNOWN;
87        boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM
88                && hasService()
89                && mDataState == TelephonyManager.DATA_CONNECTED;
90        int networkType = mServiceState.getDataNetworkType();
91        if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
92                networkType, visible ? "" : "not "));
93        try {
94            mBatteryStats.notePhoneDataConnectionState(networkType, visible);
95        } catch (RemoteException e) {
96            Log.w(TAG, "Error noting data connection state", e);
97        }
98    }
99
100    private final void updateSimState(Intent intent) {
101        String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
102        if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
103            mSimState = IccCardConstants.State.ABSENT;
104        } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
105            mSimState = IccCardConstants.State.READY;
106        } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
107            final String lockedReason =
108                    intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
109            if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
110                mSimState = IccCardConstants.State.PIN_REQUIRED;
111            } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
112                mSimState = IccCardConstants.State.PUK_REQUIRED;
113            } else {
114                mSimState = IccCardConstants.State.NETWORK_LOCKED;
115            }
116        } else {
117            mSimState = IccCardConstants.State.UNKNOWN;
118        }
119    }
120
121    private boolean isCdma() {
122        return mSignalStrength != null && !mSignalStrength.isGsm();
123    }
124
125    private boolean hasService() {
126        return mServiceState != null
127                && mServiceState.getState() != ServiceState.STATE_OUT_OF_SERVICE
128                && mServiceState.getState() != ServiceState.STATE_POWER_OFF;
129    }
130
131    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
132        @Override
133        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
134            mSignalStrength = signalStrength;
135        }
136
137        @Override
138        public void onServiceStateChanged(ServiceState state) {
139            mServiceState = state;
140            notePhoneDataConnectionState();
141        }
142
143        @Override
144        public void onDataConnectionStateChanged(int state, int networkType) {
145            mDataState = state;
146            notePhoneDataConnectionState();
147        }
148
149        @Override
150        public void onDataActivity(int direction) {
151            notePhoneDataConnectionState();
152        }
153    };
154}
155