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