1/*
2 * Copyright (C) 2016 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 */
16package com.android.internal.telephony;
17
18import android.content.BroadcastReceiver;
19import android.content.Context;
20import android.content.Intent;
21import android.content.IntentFilter;
22import android.database.ContentObserver;
23import android.os.AsyncResult;
24import android.os.Handler;
25import android.os.Message;
26import android.os.Registrant;
27import android.os.RegistrantList;
28import android.provider.Settings;
29import android.provider.Telephony;
30import android.telephony.Rlog;
31import android.telephony.TelephonyManager;
32import android.util.LocalLog;
33import android.util.Log;
34
35import com.android.internal.annotations.VisibleForTesting;
36import com.android.internal.util.IndentingPrintWriter;
37
38import java.io.FileDescriptor;
39import java.io.PrintWriter;
40
41/**
42 * Carrier Action Agent(CAA) paired with
43 * {@link com.android.internal.telephony.CarrierSignalAgent CarrierSignalAgent},
44 * serves as an agent to dispatch carrier actions from carrier apps to different telephony modules,
45 * {@link android.telephony.TelephonyManager#carrierActionSetRadioEnabled(int, boolean)
46 * carrierActionSetRadioEnabled} for example.
47 *
48 * CAA supports dynamic registration where different telephony modules could listen for a specific
49 * carrier action event and implement their own handler. CCA will dispatch the event to all
50 * interested parties and maintain the received action states internally for future inspection.
51 * Each CarrierActionAgent is associated with a phone object.
52 */
53public class CarrierActionAgent extends Handler {
54    private static final String LOG_TAG = "CarrierActionAgent";
55    private static final boolean DBG = true;
56    private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
57
58    /** A list of carrier actions */
59    public static final int CARRIER_ACTION_SET_METERED_APNS_ENABLED        = 0;
60    public static final int CARRIER_ACTION_SET_RADIO_ENABLED               = 1;
61    public static final int CARRIER_ACTION_RESET                           = 2;
62    public static final int CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS   = 3;
63    public static final int EVENT_APM_SETTINGS_CHANGED                     = 4;
64    public static final int EVENT_MOBILE_DATA_SETTINGS_CHANGED             = 5;
65    public static final int EVENT_DATA_ROAMING_OFF                         = 6;
66    public static final int EVENT_SIM_STATE_CHANGED                        = 7;
67    public static final int EVENT_APN_SETTINGS_CHANGED                     = 8;
68
69    /** Member variables */
70    private final Phone mPhone;
71    /** registrant list per carrier action */
72    private RegistrantList mMeteredApnEnableRegistrants = new RegistrantList();
73    private RegistrantList mRadioEnableRegistrants = new RegistrantList();
74    private RegistrantList mDefaultNetworkReportRegistrants = new RegistrantList();
75    /** local log for carrier actions */
76    private LocalLog mMeteredApnEnabledLog = new LocalLog(10);
77    private LocalLog mRadioEnabledLog = new LocalLog(10);
78    private LocalLog mReportDefaultNetworkStatusLog = new LocalLog(10);
79    /** carrier actions */
80    private Boolean mCarrierActionOnMeteredApnEnabled = true;
81    private Boolean mCarrierActionOnRadioEnabled = true;
82    private Boolean mCarrierActionReportDefaultNetworkStatus = false;
83    /** content observer for APM change */
84    private final SettingsObserver mSettingsObserver;
85
86    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
87        @Override
88        public void onReceive(Context context, Intent intent) {
89            final String action = intent.getAction();
90            final String iccState = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
91            if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)){
92                if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
93                    // ignore rebroadcast since carrier apps are direct boot aware.
94                    return;
95                }
96                sendMessage(obtainMessage(EVENT_SIM_STATE_CHANGED, iccState));
97            }
98        }
99    };
100
101    /** Constructor */
102    public CarrierActionAgent(Phone phone) {
103        mPhone = phone;
104        mPhone.getContext().registerReceiver(mReceiver,
105                new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED));
106        mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
107        if (DBG) log("Creating CarrierActionAgent");
108    }
109
110    @Override
111    public void handleMessage(Message msg) {
112        // skip notification if the input carrier action is same as the current one.
113        Boolean enabled = getCarrierActionEnabled(msg.what);
114        if (enabled != null && enabled == (boolean) msg.obj) return;
115        switch (msg.what) {
116            case CARRIER_ACTION_SET_METERED_APNS_ENABLED:
117                mCarrierActionOnMeteredApnEnabled = (boolean) msg.obj;
118                log("SET_METERED_APNS_ENABLED: " + mCarrierActionOnMeteredApnEnabled);
119                mMeteredApnEnabledLog.log("SET_METERED_APNS_ENABLED: "
120                        + mCarrierActionOnMeteredApnEnabled);
121                mMeteredApnEnableRegistrants.notifyRegistrants(
122                        new AsyncResult(null, mCarrierActionOnMeteredApnEnabled, null));
123                break;
124            case CARRIER_ACTION_SET_RADIO_ENABLED:
125                mCarrierActionOnRadioEnabled = (boolean) msg.obj;
126                log("SET_RADIO_ENABLED: " + mCarrierActionOnRadioEnabled);
127                mRadioEnabledLog.log("SET_RADIO_ENABLED: " + mCarrierActionOnRadioEnabled);
128                mRadioEnableRegistrants.notifyRegistrants(
129                        new AsyncResult(null, mCarrierActionOnRadioEnabled, null));
130                break;
131            case CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS:
132                mCarrierActionReportDefaultNetworkStatus = (boolean) msg.obj;
133                log("CARRIER_ACTION_REPORT_AT_DEFAULT_NETWORK_STATUS: "
134                        + mCarrierActionReportDefaultNetworkStatus);
135                mReportDefaultNetworkStatusLog.log("REGISTER_DEFAULT_NETWORK_STATUS: "
136                        + mCarrierActionReportDefaultNetworkStatus);
137                mDefaultNetworkReportRegistrants.notifyRegistrants(
138                        new AsyncResult(null, mCarrierActionReportDefaultNetworkStatus, null));
139                break;
140            case CARRIER_ACTION_RESET:
141                log("CARRIER_ACTION_RESET");
142                carrierActionReset();
143                break;
144            case EVENT_APM_SETTINGS_CHANGED:
145                log("EVENT_APM_SETTINGS_CHANGED");
146                if ((Settings.Global.getInt(mPhone.getContext().getContentResolver(),
147                        Settings.Global.AIRPLANE_MODE_ON, 0) != 0)) {
148                    carrierActionReset();
149                }
150                break;
151            case EVENT_MOBILE_DATA_SETTINGS_CHANGED:
152                log("EVENT_MOBILE_DATA_SETTINGS_CHANGED");
153                if (!mPhone.isUserDataEnabled()) carrierActionReset();
154                break;
155            case EVENT_DATA_ROAMING_OFF:
156                log("EVENT_DATA_ROAMING_OFF");
157                // reset carrier actions when exit roaming state.
158                carrierActionReset();
159                break;
160            case EVENT_SIM_STATE_CHANGED:
161                String iccState = (String) msg.obj;
162                if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(iccState)) {
163                    log("EVENT_SIM_STATE_CHANGED status: " + iccState);
164                    carrierActionReset();
165                    String mobileData = Settings.Global.MOBILE_DATA;
166                    if (TelephonyManager.getDefault().getSimCount() != 1) {
167                        mobileData = mobileData + mPhone.getSubId();
168                    }
169                    mSettingsObserver.observe(Settings.Global.getUriFor(mobileData),
170                            EVENT_MOBILE_DATA_SETTINGS_CHANGED);
171                    mSettingsObserver.observe(
172                            Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
173                            EVENT_APM_SETTINGS_CHANGED);
174                    mSettingsObserver.observe(
175                            Telephony.Carriers.CONTENT_URI, EVENT_APN_SETTINGS_CHANGED);
176                    if (mPhone.getServiceStateTracker() != null) {
177                        mPhone.getServiceStateTracker().registerForDataRoamingOff(
178                                this, EVENT_DATA_ROAMING_OFF, null, false);
179                    }
180                } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(iccState)) {
181                    log("EVENT_SIM_STATE_CHANGED status: " + iccState);
182                    carrierActionReset();
183                    mSettingsObserver.unobserve();
184                    if (mPhone.getServiceStateTracker() != null) {
185                        mPhone.getServiceStateTracker().unregisterForDataRoamingOff(this);
186                    }
187                }
188                break;
189            case EVENT_APN_SETTINGS_CHANGED:
190                log("EVENT_APN_SETTINGS_CHANGED");
191                // Reset carrier actions when APN change.
192                carrierActionReset();
193                break;
194            default:
195                loge("Unknown carrier action: " + msg.what);
196        }
197    }
198
199    /**
200     * Action set from carrier app to enable/disable radio
201     */
202    public void carrierActionSetRadioEnabled(boolean enabled) {
203        sendMessage(obtainMessage(CARRIER_ACTION_SET_RADIO_ENABLED, enabled));
204    }
205
206    /**
207     * Action set from carrier app to enable/disable metered APNs
208     */
209    public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
210        sendMessage(obtainMessage(CARRIER_ACTION_SET_METERED_APNS_ENABLED, enabled));
211    }
212
213    /**
214     * Action set from carrier app to start/stop reporting default network status.
215     */
216    public void carrierActionReportDefaultNetworkStatus(boolean report) {
217        sendMessage(obtainMessage(CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS, report));
218    }
219
220    private void carrierActionReset() {
221        carrierActionReportDefaultNetworkStatus(false);
222        carrierActionSetMeteredApnsEnabled(true);
223        carrierActionSetRadioEnabled(true);
224        // notify configured carrier apps for reset
225        mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(
226                new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET));
227    }
228
229    private RegistrantList getRegistrantsFromAction(int action) {
230        switch (action) {
231            case CARRIER_ACTION_SET_METERED_APNS_ENABLED:
232                return mMeteredApnEnableRegistrants;
233            case CARRIER_ACTION_SET_RADIO_ENABLED:
234                return mRadioEnableRegistrants;
235            case CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS:
236                return mDefaultNetworkReportRegistrants;
237            default:
238                loge("Unsupported action: " + action);
239                return null;
240        }
241    }
242
243    private Boolean getCarrierActionEnabled(int action) {
244        switch (action) {
245            case CARRIER_ACTION_SET_METERED_APNS_ENABLED:
246                return mCarrierActionOnMeteredApnEnabled;
247            case CARRIER_ACTION_SET_RADIO_ENABLED:
248                return mCarrierActionOnRadioEnabled;
249            case CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS:
250                return mCarrierActionReportDefaultNetworkStatus;
251            default:
252                loge("Unsupported action: " + action);
253                return null;
254        }
255    }
256
257    /**
258     * Register with CAA for a specific event.
259     * @param action which carrier action registrant is interested in
260     * @param notifyNow if carrier action has once set, notify registrant right after
261     *                  registering, so that registrants will get the latest carrier action.
262     */
263    public void registerForCarrierAction(int action, Handler h, int what, Object obj,
264                                         boolean notifyNow) {
265        Boolean carrierAction = getCarrierActionEnabled(action);
266        if (carrierAction == null) {
267            throw new IllegalArgumentException("invalid carrier action: " + action);
268        }
269        RegistrantList list = getRegistrantsFromAction(action);
270        Registrant r = new Registrant(h, what, obj);
271        list.add(r);
272        if (notifyNow) {
273            r.notifyRegistrant(new AsyncResult(null, carrierAction, null));
274        }
275    }
276
277    /**
278     * Unregister with CAA for a specific event. Callers will no longer be notified upon such event.
279     * @param action which carrier action caller is no longer interested in
280     */
281    public void unregisterForCarrierAction(Handler h, int action) {
282        RegistrantList list = getRegistrantsFromAction(action);
283        if (list == null) {
284            throw new IllegalArgumentException("invalid carrier action: " + action);
285        }
286        list.remove(h);
287    }
288
289    @VisibleForTesting
290    public ContentObserver getContentObserver() {
291        return mSettingsObserver;
292    }
293
294    private void log(String s) {
295        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
296    }
297
298    private void loge(String s) {
299        Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
300    }
301
302    private void logv(String s) {
303        Rlog.v(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
304    }
305
306    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
307        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
308        pw.println(" mCarrierActionOnMeteredApnsEnabled Log:");
309        ipw.increaseIndent();
310        mMeteredApnEnabledLog.dump(fd, ipw, args);
311        ipw.decreaseIndent();
312
313        pw.println(" mCarrierActionOnRadioEnabled Log:");
314        ipw.increaseIndent();
315        mRadioEnabledLog.dump(fd, ipw, args);
316        ipw.decreaseIndent();
317
318        pw.println(" mCarrierActionReportDefaultNetworkStatus Log:");
319        ipw.increaseIndent();
320        mReportDefaultNetworkStatusLog.dump(fd, ipw, args);
321        ipw.decreaseIndent();
322    }
323}
324