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