blob: 199c942ee62f3fd75b757f227fd701872f729740 [file] [log] [blame]
Jonathan Basseri6465afd2015-02-25 13:05:57 -08001/**
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
17
18package com.android.phone;
19
20import static android.Manifest.permission.READ_PHONE_STATE;
21import static com.android.internal.telephony.uicc.IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED;
22
23import android.app.ActivityManagerNative;
24import android.content.BroadcastReceiver;
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.ServiceConnection;
30import android.database.sqlite.SQLiteDatabase;
31import android.database.sqlite.SQLiteOpenHelper;
32import android.os.AsyncResult;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.IBinder;
36import android.os.Message;
37import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.UserHandle;
40import android.service.carrier.CarrierConfigService;
41import android.service.carrier.CarrierIdentifier;
42import android.service.carrier.ICarrierConfigService;
43import android.telephony.CarrierConfigManager;
44import android.telephony.SubscriptionManager;
45import android.telephony.TelephonyManager;
46import android.util.Log;
47
48import com.android.internal.telephony.ICarrierConfigLoader;
49import com.android.internal.telephony.IccCardConstants;
50import com.android.internal.telephony.Phone;
51import com.android.internal.telephony.PhoneConstants;
52import com.android.internal.telephony.PhoneFactory;
53import com.android.internal.telephony.TelephonyIntents;
54
55import java.util.List;
56
57/**
58 * CarrierConfigLoader binds to privileged carrier apps to fetch carrier config overlays.
59 * TODO: implement persist cache
60 * TODO: add gid2 to phone
61 * TODO: handle package install/uninstall events
62 */
63
64public class CarrierConfigLoader extends ICarrierConfigLoader.Stub {
65 private static final String TAG = "CarrierConfigLoader";
66 // Package name for default carrier config app, bundled with system image.
67 private static final String DEFAULT_CARRIER_CONFIG_PACKAGE = "com.android.carrierconfig";
68
69 /** The singleton instance. */
70 private static CarrierConfigLoader sInstance;
71 // The context for phone app, passed from PhoneGlobals.
72 private Context mContext;
73 // Carrier configs from default app, indexed by phoneID.
74 private Bundle[] mConfigFromDefaultApp;
75 // Carrier configs from privileged carrier config app, indexed by phoneID.
76 private Bundle[] mConfigFromCarrierApp;
77 // Service connection for binding to config app.
78 private ConfigServiceConnection[] mServiceConnection;
79
80 // Broadcast receiver for SIM and pkg intents, register intent filter in constructor.
81 private final BroadcastReceiver mReceiver = new ConfigLoaderBroadcastReceiver();
82
83 // Message codes; see mHandler below.
84 // Request from SubscriptionInfoUpdater when SIM becomes absent or error.
85 private static final int EVENT_CLEAR_CONFIG = 0;
86 // Request from SubscriptionInfoUpdater to update config.
87 private static final int EVENT_UPDATE_CONFIG = 1;
88 // Request from carrier app to reload config.
89 private static final int EVENT_RELOAD_CONFIG = 2;
90 // Has connected to default app.
91 private static final int EVENT_CONNECTED_TO_DEFAULT = 3;
92 // Has connected to carrier app.
93 private static final int EVENT_CONNECTED_TO_CARRIER = 4;
94 // Config has been loaded from default app.
95 private static final int EVENT_LOADED_FROM_DEFAULT = 5;
96 // Config has been loaded from carrier app.
97 private static final int EVENT_LOADED_FROM_CARRIER = 6;
98
99
100 // Handler to process various events.
101 // For each phoneId, state transition should be: default app bind->connected->loaded,
102 // carrier app (if exists) bind-> connected->loaded. At any time, at most one connection is active.
103 // If events are not in this order, previous connection will be unbind, so only latest event takes effect.
104 // We broadcast config change when:
105 // 1. loaded from carrier app
106 // 2. loaded from default app if no carrier app
107 // 3. config cleared, possibly due to sim removed
108 // 4. bind or IPC error
109 private Handler mHandler = new Handler() {
110 @Override
111 public void handleMessage(Message msg) {
112 int phoneId = msg.arg1;
113 log("mHandler: " + msg.what + " phoneId: " + phoneId);
114 CarrierIdentifier carrierId;
115 ConfigServiceConnection conn;
116 Bundle config;
117 switch (msg.what) {
118 case EVENT_CLEAR_CONFIG:
119 mConfigFromDefaultApp[phoneId] = null;
120 mConfigFromCarrierApp[phoneId] = null;
121 mServiceConnection[phoneId] = null;
122 broadcastConfigChangedIntent(phoneId);
123 break;
124 case EVENT_UPDATE_CONFIG:
125 // Use persist cache to avoid loading from app.
126 // Fall through to next event if cache not hit.
127 case EVENT_RELOAD_CONFIG:
128 if (!bindToConfigPackage(DEFAULT_CARRIER_CONFIG_PACKAGE,
129 phoneId, EVENT_CONNECTED_TO_DEFAULT)) {
130 //Send bcast if bind fails
131 broadcastConfigChangedIntent(phoneId);
132 }
133 break;
134
135 case EVENT_CONNECTED_TO_DEFAULT:
136 carrierId = getCarrierIdForPhoneId(phoneId);
137 conn = (ConfigServiceConnection) msg.obj;
138 // If new service connection has been created, unbind.
139 if (mServiceConnection[phoneId] != conn || conn.service == null) {
140 mContext.unbindService(conn);
141 break;
142 }
143 try {
144 ICarrierConfigService configService = ICarrierConfigService.Stub.asInterface(conn.service);
145 config = configService.getCarrierConfig(carrierId);
146 mConfigFromDefaultApp[phoneId] = config;
147 mHandler.sendMessage(mHandler.obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId));
148 } catch (RemoteException ex) {
149 Log.e(TAG, "Failed to get carrier config: " + ex.toString());
150 } finally {
151 mContext.unbindService(mServiceConnection[phoneId]);
152 }
153 break;
154
155 case EVENT_LOADED_FROM_DEFAULT:
156 if (mServiceConnection[phoneId] == null) {
157 break;
158 }
159 List<String> carrierPackageNames = TelephonyManager.from(mContext)
Junda Liu0bdd55b2015-05-04 14:32:58 -0700160 .getCarrierPackageNamesForIntentAndPhone(
161 new Intent(CarrierConfigService.SERVICE_INTERFACE), phoneId);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800162 log("Found carrier config app: " + carrierPackageNames);
163 if (carrierPackageNames != null && carrierPackageNames.size() > 0) {
164 if (!bindToConfigPackage(carrierPackageNames.get(0),
165 phoneId, EVENT_CONNECTED_TO_CARRIER)) {
166 broadcastConfigChangedIntent(phoneId);
167 }
168 } else {
169 broadcastConfigChangedIntent(phoneId);
170 }
171 break;
172
173 case EVENT_CONNECTED_TO_CARRIER:
174 carrierId = getCarrierIdForPhoneId(phoneId);
175 conn = (ConfigServiceConnection) msg.obj;
176 // If new service connection has been created, unbind.
177 if (mServiceConnection[phoneId] != conn ||
178 conn.service == null) {
179 mContext.unbindService(conn);
180 break;
181 }
182 try {
183 ICarrierConfigService configService = ICarrierConfigService.Stub.asInterface(conn.service);
184 config = configService.getCarrierConfig(carrierId);
185 mConfigFromCarrierApp[phoneId] = config;
186 mHandler.sendMessage(mHandler.obtainMessage(EVENT_LOADED_FROM_CARRIER, phoneId));
187 } catch (RemoteException ex) {
188 Log.e(TAG, "Failed to get carrier config: " + ex.toString());
189 } finally {
190 mContext.unbindService(mServiceConnection[phoneId]);
191 }
192 break;
193
194 case EVENT_LOADED_FROM_CARRIER:
195 if (mServiceConnection[phoneId] == null) {
196 break;
197 }
198 broadcastConfigChangedIntent(phoneId);
199 break;
200 }
201 }
202 };
203
204 /**
205 * Constructs a CarrierConfigLoader, registers it as a service, and registers a broadcast
206 * receiver for relevant events.
207 */
208 private CarrierConfigLoader(Context context) {
209 mContext = context;
210
211 // Register for package updates.
212 IntentFilter triggers = new IntentFilter();
213 triggers.addAction(Intent.ACTION_PACKAGE_ADDED);
214 triggers.addAction(Intent.ACTION_PACKAGE_CHANGED);
215 triggers.addAction(Intent.ACTION_PACKAGE_REMOVED);
216 mContext.registerReceiver(mReceiver, triggers);
217
218 int numPhones = TelephonyManager.from(context).getPhoneCount();
219 mConfigFromDefaultApp = new Bundle[numPhones];
220 mConfigFromCarrierApp = new Bundle[numPhones];
221 mServiceConnection = new ConfigServiceConnection[numPhones];
222 // Make this service available through ServiceManager.
223 ServiceManager.addService(Context.CARRIER_CONFIG_SERVICE, this);
224 log("CarrierConfigLoader has started");
225 }
226
227 /**
228 * Initialize the singleton CarrierConfigLoader instance.
229 *
230 * This is only done once, at startup, from {@link com.android.phone.PhoneApp#onCreate}.
231 */
232 /* package */
233 static CarrierConfigLoader init(Context context) {
234 synchronized (CarrierConfigLoader.class) {
235 if (sInstance == null) {
236 sInstance = new CarrierConfigLoader(context);
237 } else {
238 Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
239 }
240 return sInstance;
241 }
242 }
243
244 private void broadcastConfigChangedIntent(int phoneId) {
245 Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
246 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
247 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
248 ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE,
249 UserHandle.USER_ALL);
250 }
251
252 /** Binds to the default or carrier config app. */
253 private boolean bindToConfigPackage(String pkgName, int phoneId, int eventId) {
254 log("Binding to " + pkgName + " for phone " + phoneId);
255 Intent carrierConfigService = new Intent(CarrierConfigService.SERVICE_INTERFACE);
256 carrierConfigService.setPackage(pkgName);
257 mServiceConnection[phoneId] = new ConfigServiceConnection(phoneId, eventId);
258 try {
259 return mContext.bindService(carrierConfigService, mServiceConnection[phoneId],
260 Context.BIND_AUTO_CREATE);
261 } catch (SecurityException ex) {
262 return false;
263 }
264 }
265
266 private CarrierIdentifier getCarrierIdForPhoneId(int phoneId) {
267 String mcc = "";
268 String mnc = "";
269 String imsi = "";
270 String gid1 = "";
271 String gid2 = "";
272 String spn = TelephonyManager.from(mContext).getSimOperatorNameForPhone(phoneId);
273 String simOperator = TelephonyManager.from(mContext).getSimOperatorNumericForPhone(phoneId);
Jonathan Basseri1fa437c2015-04-20 11:08:18 -0700274 // A valid simOperator should be 5 or 6 digits, depending on the length of the MNC.
275 if (simOperator != null && simOperator.length() >= 3) {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800276 mcc = simOperator.substring(0, 3);
277 mnc = simOperator.substring(3);
278 }
279 Phone phone = PhoneFactory.getPhone(phoneId);
280 if (phone != null) {
281 imsi = phone.getSubscriberId();
282 gid1 = phone.getGroupIdLevel1();
283 // add gid2 after phone supports it.
284 }
285
286 return new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2);
287 }
288
289 @Override
290 public Bundle getConfigForSubId(int subId) {
291 int phoneId = SubscriptionManager.getPhoneId(subId);
292 Bundle retConfig = CarrierConfigManager.getDefaultConfig();
293 if (SubscriptionManager.isValidPhoneId(phoneId)) {
294 Bundle config = mConfigFromDefaultApp[phoneId];
295 if (config != null) retConfig.putAll(config);
296 config = mConfigFromCarrierApp[phoneId];
297 if (config != null) retConfig.putAll(config);
298 }
299 return retConfig;
300 }
301
302 @Override
303 public void reloadCarrierConfigForSubId(int subId) {
304 int phoneId = SubscriptionManager.getPhoneId(subId);
305 if (SubscriptionManager.isValidPhoneId(phoneId)) {
306 mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELOAD_CONFIG, phoneId));
307 } else {
308 log("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
309 }
310 }
311
312 @Override
313 public void updateConfigForPhoneId(int phoneId, String simState) {
314 log("update config for phoneId: " + phoneId + " simState: " + simState);
315 if (!SubscriptionManager.isValidPhoneId(phoneId)) {
316 return;
317 }
318 // requires Java 7 for switch on string.
319 switch (simState) {
320 case IccCardConstants.INTENT_VALUE_ICC_ABSENT:
321 case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR:
322 case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN:
323 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CLEAR_CONFIG, phoneId));
324 break;
325 case IccCardConstants.INTENT_VALUE_ICC_LOADED:
326 case IccCardConstants.INTENT_VALUE_ICC_LOCKED:
327 mHandler.sendMessage(mHandler.obtainMessage(EVENT_UPDATE_CONFIG, phoneId));
328 break;
329 }
330 }
331
332 private class ConfigServiceConnection implements ServiceConnection {
333 int phoneId;
334 int eventId;
335 IBinder service;
336
337 public ConfigServiceConnection(int phoneId, int eventId) {
338 this.phoneId = phoneId;
339 this.eventId = eventId;
340 }
341
342 @Override
343 public void onServiceConnected(ComponentName name, IBinder service) {
344 log("Connected to config app: " + name.flattenToString());
345 this.service = service;
346 mHandler.sendMessage(mHandler.obtainMessage(eventId, phoneId, -1, this));
347 }
348
349 @Override
350 public void onServiceDisconnected(ComponentName name) {
351 this.service = null;
352 }
353 }
354
355 private class ConfigLoaderBroadcastReceiver extends BroadcastReceiver {
356 @Override
357 public void onReceive(Context context, Intent intent) {
358 String action = intent.getAction();
359 log("Receive action: " + action);
360 }
361 }
362
363 private static void log(String msg) {
364 Log.d(TAG, msg);
365 }
366}
367