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