1 /* 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 package com.android.internal.telephony; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.content.res.XmlResourceParser; 23 import android.database.Cursor; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.system.ErrnoException; 27 import android.system.Os; 28 import android.system.OsConstants; 29 import android.system.StructStatVfs; 30 import android.telephony.AccessNetworkConstants.TransportType; 31 import android.text.TextUtils; 32 33 import com.android.ims.ImsManager; 34 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; 35 import com.android.internal.telephony.cdma.EriManager; 36 import com.android.internal.telephony.data.AccessNetworksManager; 37 import com.android.internal.telephony.data.DataNetworkController; 38 import com.android.internal.telephony.data.DataProfileManager; 39 import com.android.internal.telephony.data.DataServiceManager; 40 import com.android.internal.telephony.data.DataSettingsManager; 41 import com.android.internal.telephony.data.LinkBandwidthEstimator; 42 import com.android.internal.telephony.data.PhoneSwitcher; 43 import com.android.internal.telephony.dataconnection.DataEnabledSettings; 44 import com.android.internal.telephony.dataconnection.DcTracker; 45 import com.android.internal.telephony.dataconnection.TransportManager; 46 import com.android.internal.telephony.emergency.EmergencyNumberTracker; 47 import com.android.internal.telephony.imsphone.ImsExternalCallTracker; 48 import com.android.internal.telephony.imsphone.ImsPhone; 49 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; 50 import com.android.internal.telephony.nitz.NitzStateMachineImpl; 51 import com.android.internal.telephony.uicc.IccCardStatus; 52 import com.android.internal.telephony.uicc.UiccCard; 53 import com.android.internal.telephony.uicc.UiccProfile; 54 import com.android.telephony.Rlog; 55 56 import dalvik.system.PathClassLoader; 57 58 import org.xmlpull.v1.XmlPullParser; 59 import org.xmlpull.v1.XmlPullParserException; 60 61 import java.io.File; 62 import java.io.IOException; 63 import java.util.Arrays; 64 import java.util.HashSet; 65 import java.util.Set; 66 import java.util.function.Consumer; 67 import java.util.stream.Collectors; 68 69 /** 70 * This class has one-line methods to instantiate objects only. The purpose is to make code 71 * unit-test friendly and use this class as a way to do dependency injection. Instantiating objects 72 * this way makes it easier to mock them in tests. 73 */ 74 public class TelephonyComponentFactory { 75 76 private static final String TAG = TelephonyComponentFactory.class.getSimpleName(); 77 78 private static TelephonyComponentFactory sInstance; 79 private final TelephonyFacade mTelephonyFacade = new TelephonyFacade(); 80 81 private InjectedComponents mInjectedComponents; 82 83 private static class InjectedComponents { 84 private static final String ATTRIBUTE_JAR = "jar"; 85 private static final String ATTRIBUTE_PACKAGE = "package"; 86 private static final String TAG_INJECTION = "injection"; 87 private static final String TAG_COMPONENTS = "components"; 88 private static final String TAG_COMPONENT = "component"; 89 private static final String SYSTEM = "/system/"; 90 private static final String PRODUCT = "/product/"; 91 private static final String SYSTEM_EXT = "/system_ext/"; 92 93 private final Set<String> mComponentNames = new HashSet<>(); 94 private TelephonyComponentFactory mInjectedInstance; 95 private String mPackageName; 96 private String mJarPath; 97 98 /** 99 * @return paths correctly configured to inject. 100 * 1) PackageName and JarPath mustn't be empty. 101 * 2) JarPath is restricted under /system or /product or /system_ext only. 102 * 3) JarPath is on a READ-ONLY partition. 103 */ getValidatedPaths()104 private @Nullable String getValidatedPaths() { 105 if (TextUtils.isEmpty(mPackageName) || TextUtils.isEmpty(mJarPath)) { 106 return null; 107 } 108 // filter out invalid paths 109 return Arrays.stream(mJarPath.split(File.pathSeparator)) 110 .filter(s -> (s.startsWith(SYSTEM) || s.startsWith(PRODUCT) 111 || s.startsWith(SYSTEM_EXT))) 112 .filter(s -> { 113 try { 114 // This will also throw an error if the target doesn't exist. 115 StructStatVfs vfs = Os.statvfs(s); 116 return (vfs.f_flag & OsConstants.ST_RDONLY) != 0; 117 } catch (ErrnoException e) { 118 Rlog.w(TAG, "Injection jar is not protected , path: " + s 119 + e.getMessage()); 120 return false; 121 } 122 }).distinct() 123 .collect(Collectors.joining(File.pathSeparator)); 124 } 125 makeInjectedInstance()126 private void makeInjectedInstance() { 127 String validatedPaths = getValidatedPaths(); 128 Rlog.d(TAG, "validated paths: " + validatedPaths); 129 if (!TextUtils.isEmpty(validatedPaths)) { 130 try { 131 PathClassLoader classLoader = new PathClassLoader(validatedPaths, 132 ClassLoader.getSystemClassLoader()); 133 Class<?> cls = classLoader.loadClass(mPackageName); 134 mInjectedInstance = (TelephonyComponentFactory) cls.newInstance(); 135 } catch (ClassNotFoundException e) { 136 Rlog.e(TAG, "failed: " + e.getMessage()); 137 } catch (IllegalAccessException | InstantiationException e) { 138 Rlog.e(TAG, "injection failed: " + e.getMessage()); 139 } 140 } 141 } 142 isComponentInjected(String componentName)143 private boolean isComponentInjected(String componentName) { 144 if (mInjectedInstance == null) { 145 return false; 146 } 147 return mComponentNames.contains(componentName); 148 } 149 150 /** 151 * Find the injection tag, set attributes, and then parse the injection. 152 */ parseXml(@onNull XmlPullParser parser)153 private void parseXml(@NonNull XmlPullParser parser) { 154 parseXmlByTag(parser, false, p -> { 155 setAttributes(p); 156 parseInjection(p); 157 }, TAG_INJECTION); 158 } 159 160 /** 161 * Only parse the first injection tag. Find the components tag, then try parse it next. 162 */ parseInjection(@onNull XmlPullParser parser)163 private void parseInjection(@NonNull XmlPullParser parser) { 164 parseXmlByTag(parser, false, p -> parseComponents(p), TAG_COMPONENTS); 165 } 166 167 /** 168 * Only parse the first components tag. Find the component tags, then try parse them next. 169 */ parseComponents(@onNull XmlPullParser parser)170 private void parseComponents(@NonNull XmlPullParser parser) { 171 parseXmlByTag(parser, true, p -> parseComponent(p), TAG_COMPONENT); 172 } 173 174 /** 175 * Extract text values from component tags. 176 */ parseComponent(@onNull XmlPullParser parser)177 private void parseComponent(@NonNull XmlPullParser parser) { 178 try { 179 int outerDepth = parser.getDepth(); 180 int type; 181 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 182 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 183 if (type == XmlPullParser.TEXT) { 184 mComponentNames.add(parser.getText()); 185 } 186 } 187 } catch (XmlPullParserException | IOException e) { 188 Rlog.e(TAG, "Failed to parse the component." , e); 189 } 190 } 191 192 /** 193 * Iterates the tags, finds the corresponding tag and then applies the consumer. 194 */ parseXmlByTag(@onNull XmlPullParser parser, boolean allowDuplicate, @NonNull Consumer<XmlPullParser> consumer, @NonNull final String tag)195 private void parseXmlByTag(@NonNull XmlPullParser parser, boolean allowDuplicate, 196 @NonNull Consumer<XmlPullParser> consumer, @NonNull final String tag) { 197 try { 198 int outerDepth = parser.getDepth(); 199 int type; 200 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 201 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 202 if (type == XmlPullParser.START_TAG && tag.equals(parser.getName())) { 203 consumer.accept(parser); 204 if (!allowDuplicate) { 205 return; 206 } 207 } 208 } 209 } catch (XmlPullParserException | IOException e) { 210 Rlog.e(TAG, "Failed to parse or find tag: " + tag, e); 211 } 212 } 213 214 /** 215 * Sets the mPackageName and mJarPath by <injection/> tag. 216 * @param parser 217 * @return 218 */ setAttributes(@onNull XmlPullParser parser)219 private void setAttributes(@NonNull XmlPullParser parser) { 220 for (int i = 0; i < parser.getAttributeCount(); i++) { 221 String name = parser.getAttributeName(i); 222 String value = parser.getAttributeValue(i); 223 if (InjectedComponents.ATTRIBUTE_PACKAGE.equals(name)) { 224 mPackageName = value; 225 } else if (InjectedComponents.ATTRIBUTE_JAR.equals(name)) { 226 mJarPath = value; 227 } 228 } 229 } 230 } 231 232 public static TelephonyComponentFactory getInstance() { 233 if (sInstance == null) { 234 sInstance = new TelephonyComponentFactory(); 235 } 236 return sInstance; 237 } 238 239 /** 240 * Inject TelephonyComponentFactory using a xml config file. 241 * @param parser a nullable {@link XmlResourceParser} created with the injection config file. 242 * The config xml should has below formats: 243 * <injection package="package.InjectedTelephonyComponentFactory" jar="path to jar file"> 244 * <components> 245 * <component>example.package.ComponentAbc</component> 246 * <component>example.package.ComponentXyz</component> 247 * <!-- e.g. com.android.internal.telephony.GsmCdmaPhone --> 248 * </components> 249 * </injection> 250 */ 251 public void injectTheComponentFactory(XmlResourceParser parser) { 252 if (mInjectedComponents != null) { 253 Rlog.d(TAG, "Already injected."); 254 return; 255 } 256 257 if (parser != null) { 258 mInjectedComponents = new InjectedComponents(); 259 mInjectedComponents.parseXml(parser); 260 mInjectedComponents.makeInjectedInstance(); 261 boolean injectSuccessful = !TextUtils.isEmpty(mInjectedComponents.getValidatedPaths()); 262 Rlog.d(TAG, "Total components injected: " + (injectSuccessful 263 ? mInjectedComponents.mComponentNames.size() : 0)); 264 } 265 } 266 267 /** 268 * Use the injected TelephonyComponentFactory if configured. Otherwise, use the default. 269 * @param componentName Name of the component class uses the injected component factory, 270 * e.g. GsmCdmaPhone.class.getName() for {@link GsmCdmaPhone} 271 * @return injected component factory. If not configured or injected, return the default one. 272 */ 273 public TelephonyComponentFactory inject(String componentName) { 274 if (mInjectedComponents != null && mInjectedComponents.isComponentInjected(componentName)) { 275 return mInjectedComponents.mInjectedInstance; 276 } 277 return sInstance; 278 } 279 280 public GsmCdmaCallTracker makeGsmCdmaCallTracker(GsmCdmaPhone phone) { 281 return new GsmCdmaCallTracker(phone); 282 } 283 284 public SmsStorageMonitor makeSmsStorageMonitor(Phone phone) { 285 return new SmsStorageMonitor(phone); 286 } 287 288 public SmsUsageMonitor makeSmsUsageMonitor(Context context) { 289 return new SmsUsageMonitor(context); 290 } 291 292 public ServiceStateTracker makeServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) { 293 return new ServiceStateTracker(phone, ci); 294 } 295 296 /** 297 * Create a new EmergencyNumberTracker. 298 */ 299 public EmergencyNumberTracker makeEmergencyNumberTracker(Phone phone, CommandsInterface ci) { 300 return new EmergencyNumberTracker(phone, ci); 301 } 302 303 private static final boolean USE_NEW_NITZ_STATE_MACHINE = true; 304 305 /** 306 * Returns a new {@link NitzStateMachine} instance. 307 */ 308 public NitzStateMachine makeNitzStateMachine(GsmCdmaPhone phone) { 309 return NitzStateMachineImpl.createInstance(phone); 310 } 311 312 public SimActivationTracker makeSimActivationTracker(Phone phone) { 313 return new SimActivationTracker(phone); 314 } 315 316 public DcTracker makeDcTracker(Phone phone, @TransportType int transportType) { 317 return new DcTracker(phone, transportType); 318 } 319 320 public CarrierSignalAgent makeCarrierSignalAgent(Phone phone) { 321 return new CarrierSignalAgent(phone); 322 } 323 324 public CarrierActionAgent makeCarrierActionAgent(Phone phone) { 325 return new CarrierActionAgent(phone); 326 } 327 328 public CarrierResolver makeCarrierResolver(Phone phone) { 329 return new CarrierResolver(phone); 330 } 331 332 public IccPhoneBookInterfaceManager makeIccPhoneBookInterfaceManager(Phone phone) { 333 return new IccPhoneBookInterfaceManager(phone); 334 } 335 336 public IccSmsInterfaceManager makeIccSmsInterfaceManager(Phone phone) { 337 return new IccSmsInterfaceManager(phone); 338 } 339 340 /** 341 * Create a new UiccProfile object. 342 */ 343 public UiccProfile makeUiccProfile(Context context, CommandsInterface ci, IccCardStatus ics, 344 int phoneId, UiccCard uiccCard, Object lock) { 345 return new UiccProfile(context, ci, ics, phoneId, uiccCard, lock); 346 } 347 348 public EriManager makeEriManager(Phone phone, int eriFileSource) { 349 return new EriManager(phone, eriFileSource); 350 } 351 352 public WspTypeDecoder makeWspTypeDecoder(byte[] pdu) { 353 return new WspTypeDecoder(pdu); 354 } 355 356 /** 357 * Create a tracker for a single-part SMS. 358 */ 359 public InboundSmsTracker makeInboundSmsTracker(Context context, byte[] pdu, long timestamp, 360 int destPort, boolean is3gpp2, boolean is3gpp2WapPdu, String address, 361 String displayAddr, String messageBody, boolean isClass0, int subId, 362 @InboundSmsHandler.SmsSource int smsSource) { 363 return new InboundSmsTracker(context, pdu, timestamp, destPort, is3gpp2, is3gpp2WapPdu, 364 address, displayAddr, messageBody, isClass0, subId, smsSource); 365 } 366 367 /** 368 * Create a tracker for a multi-part SMS. 369 */ 370 public InboundSmsTracker makeInboundSmsTracker(Context context, byte[] pdu, long timestamp, 371 int destPort, boolean is3gpp2, String address, String displayAddr, int referenceNumber, 372 int sequenceNumber, int messageCount, boolean is3gpp2WapPdu, String messageBody, 373 boolean isClass0, int subId, @InboundSmsHandler.SmsSource int smsSource) { 374 return new InboundSmsTracker(context, pdu, timestamp, destPort, is3gpp2, address, 375 displayAddr, referenceNumber, sequenceNumber, messageCount, is3gpp2WapPdu, 376 messageBody, isClass0, subId, smsSource); 377 } 378 379 /** 380 * Create a tracker from a row of raw table 381 */ 382 public InboundSmsTracker makeInboundSmsTracker(Context context, Cursor cursor, 383 boolean isCurrentFormat3gpp2) { 384 return new InboundSmsTracker(context, cursor, isCurrentFormat3gpp2); 385 } 386 387 public ImsPhoneCallTracker makeImsPhoneCallTracker(ImsPhone imsPhone) { 388 return new ImsPhoneCallTracker(imsPhone, ImsManager::getConnector); 389 } 390 391 public ImsExternalCallTracker makeImsExternalCallTracker(ImsPhone imsPhone) { 392 393 return new ImsExternalCallTracker(imsPhone, imsPhone.getContext().getMainExecutor()); 394 } 395 396 /** 397 * Create an AppSmsManager for per-app SMS message. 398 */ 399 public AppSmsManager makeAppSmsManager(Context context) { 400 return new AppSmsManager(context); 401 } 402 403 public DeviceStateMonitor makeDeviceStateMonitor(Phone phone) { 404 return new DeviceStateMonitor(phone); 405 } 406 407 public TransportManager makeTransportManager(Phone phone) { 408 return new TransportManager(phone); 409 } 410 411 /** 412 * Make access networks manager 413 * 414 * @param phone The phone instance 415 * @param looper Looper for the handler. 416 * @return The access networks manager 417 */ 418 public AccessNetworksManager makeAccessNetworksManager(Phone phone, Looper looper) { 419 return new AccessNetworksManager(phone, looper); 420 } 421 422 public CdmaSubscriptionSourceManager 423 getCdmaSubscriptionSourceManagerInstance(Context context, CommandsInterface ci, Handler h, 424 int what, Object obj) { 425 return CdmaSubscriptionSourceManager.getInstance(context, ci, h, what, obj); 426 } 427 428 public LocaleTracker makeLocaleTracker(Phone phone, NitzStateMachine nitzStateMachine, 429 Looper looper) { 430 return new LocaleTracker(phone, nitzStateMachine, looper); 431 } 432 433 public DataEnabledSettings makeDataEnabledSettings(Phone phone) { 434 return new DataEnabledSettings(phone); 435 } 436 437 public Phone makePhone(Context context, CommandsInterface ci, PhoneNotifier notifier, 438 int phoneId, int precisePhoneType, 439 TelephonyComponentFactory telephonyComponentFactory) { 440 return new GsmCdmaPhone(context, ci, notifier, phoneId, precisePhoneType, 441 telephonyComponentFactory); 442 } 443 444 public SubscriptionController initSubscriptionController(Context c) { 445 return SubscriptionController.init(c); 446 } 447 448 public PhoneSwitcher makePhoneSwitcher(int maxDataAttachModemCount, Context context, 449 Looper looper) { 450 return PhoneSwitcher.make(maxDataAttachModemCount, context, looper); 451 } 452 453 /** 454 * Create a new DisplayInfoController. 455 */ 456 public DisplayInfoController makeDisplayInfoController(Phone phone) { 457 return new DisplayInfoController(phone); 458 } 459 460 public MultiSimSettingController initMultiSimSettingController(Context c, 461 SubscriptionController sc) { 462 return MultiSimSettingController.init(c, sc); 463 } 464 465 /** 466 * Create a new SignalStrengthController instance. 467 */ 468 public SignalStrengthController makeSignalStrengthController(GsmCdmaPhone phone) { 469 return new SignalStrengthController(phone); 470 } 471 472 public SubscriptionInfoUpdater makeSubscriptionInfoUpdater(Looper looper, Context context, 473 SubscriptionController sc) { 474 return new SubscriptionInfoUpdater(looper, context, sc); 475 } 476 477 /** 478 * Create a new LinkBandwidthEstimator. 479 */ 480 public LinkBandwidthEstimator makeLinkBandwidthEstimator(Phone phone) { 481 return new LinkBandwidthEstimator(phone, mTelephonyFacade); 482 } 483 484 /** 485 * Create a new data network controller instance. The instance is per-SIM. On multi-sim devices, 486 * there will be multiple {@link DataNetworkController} instances. 487 * 488 * @param phone The phone object 489 * @param looper The looper for event handling 490 * @return The data network controller instance 491 */ 492 public DataNetworkController makeDataNetworkController(Phone phone, Looper looper) { 493 return new DataNetworkController(phone, looper); 494 } 495 496 /** 497 * Create data profile manager. 498 * 499 * @param phone The phone instance. 500 * @param dataNetworkController Data network controller instance. 501 * @param dataServiceManager Data service manager instance. 502 * @param looper The looper to be used by the handler. Currently the handler thread is the phone 503 * process's main thread. 504 * @param callback Callback for passing events back to data network controller. 505 * @return The data profile manager instance. 506 */ 507 public @NonNull DataProfileManager makeDataProfileManager(@NonNull Phone phone, 508 @NonNull DataNetworkController dataNetworkController, 509 @NonNull DataServiceManager dataServiceManager, @NonNull Looper looper, 510 @NonNull DataProfileManager.DataProfileManagerCallback callback) { 511 return new DataProfileManager(phone, dataNetworkController, dataServiceManager, looper, 512 callback); 513 } 514 515 /** 516 * Create data settings manager. 517 * 518 * @param phone The phone instance. 519 * @param dataNetworkController Data network controller instance. 520 * @param looper The looper to be used by the handler. Currently the handler thread is the phone 521 * process's main thread. 522 * @param callback Callback for passing events back to data network controller. 523 * @return The data settings manager instance. 524 */ 525 public @NonNull DataSettingsManager makeDataSettingsManager(@NonNull Phone phone, 526 @NonNull DataNetworkController dataNetworkController, @NonNull Looper looper, 527 @NonNull DataSettingsManager.DataSettingsManagerCallback callback) { 528 return new DataSettingsManager(phone, dataNetworkController, looper, callback); 529 } 530 } 531