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