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