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