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