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