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