• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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