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