From 963e84fc98df48359f24103a9696fb641e51a39f Mon Sep 17 00:00:00 2001 From: Olaia Segovia Date: Thu, 16 Mar 2017 08:40:30 +0100 Subject: (1/2) Make sensitive phone numbers not to be shown in call log history. Considering sensitive phone numbers to be, hotlines dealing with violence against women. In the EU, these numbers do not show up in the phone bill. In order to avoid these phone numbers to be listed in the Call Log, we have implemented a new XML file which is parsed with phone numbers from different countries to be filtered. This file needs to be copied to device via desired Android.mk file in order to be able to process it. The comparison is made checking the network MCC the SIM is connected to in order to consider roaming and multisim scenarios. Test: CallLogManagerTest.testDontLogCallsToSensitivePhoneNumber PASS Change-Id: I4a59ff0577942ce56924f1a434ae0a3a38eacc62 Signed-off-by: Olaia Segovia --- src/com/android/server/telecom/CallLogManager.java | 9 +- .../server/telecom/SensitivePhoneNumber.java | 90 ++++++++++++++++ .../server/telecom/SensitivePhoneNumbers.java | 116 +++++++++++++++++++++ .../server/telecom/tests/CallLogManagerTest.java | 49 +++++++++ 4 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 src/com/android/server/telecom/SensitivePhoneNumber.java create mode 100644 src/com/android/server/telecom/SensitivePhoneNumbers.java diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java index ba03e5cd..1a1e6d54 100755 --- a/src/com/android/server/telecom/CallLogManager.java +++ b/src/com/android/server/telecom/CallLogManager.java @@ -124,6 +124,7 @@ public final class CallLogManager extends CallsManagerListenerBase { private Object mLock; private String mCurrentCountryIso; + private SensitivePhoneNumbers mSensitivePhoneNumbers; public CallLogManager(Context context, PhoneAccountRegistrar phoneAccountRegistrar, MissedCallNotifier missedCallNotifier) { @@ -131,6 +132,7 @@ public final class CallLogManager extends CallsManagerListenerBase { mPhoneAccountRegistrar = phoneAccountRegistrar; mMissedCallNotifier = missedCallNotifier; mLock = new Object(); + mSensitivePhoneNumbers = new SensitivePhoneNumbers(); } @Override @@ -278,8 +280,11 @@ public final class CallLogManager extends CallsManagerListenerBase { CarrierConfigManager.KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL); } - // Don't log emergency numbers if the device doesn't allow it. - final boolean isOkToLogThisCall = !isEmergency || okToLogEmergencyNumber; + // Don't log emergency nor sensitive numbers if the device doesn't allow it. + boolean isSensitiveNumber = mSensitivePhoneNumbers.isSensitiveNumber(mContext, number, + accountHandle.getId()); + Log.d(TAG, "isSensitiveNumber: "+ isSensitiveNumber); + final boolean isOkToLogThisCall = (!isEmergency || okToLogEmergencyNumber) && !isSensitiveNumber; sendAddCallBroadcast(callType, duration); diff --git a/src/com/android/server/telecom/SensitivePhoneNumber.java b/src/com/android/server/telecom/SensitivePhoneNumber.java new file mode 100644 index 00000000..d0eb936d --- /dev/null +++ b/src/com/android/server/telecom/SensitivePhoneNumber.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2017 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.telecom; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; + +public class SensitivePhoneNumber { + private static final String LOG_TAG = "SensitivePhoneNumber"; + private static final String ns = null; + + private String networkNumeric; + private ArrayList phoneNumbers; + + public SensitivePhoneNumber(String networkNumeric, ArrayList phoneNumbers) { + this.networkNumeric = networkNumeric; + this.phoneNumbers = phoneNumbers; + } + + public String getNetworkNumeric() { + return networkNumeric; + } + + public ArrayList getPhoneNumbers() { + return phoneNumbers; + } + + public void setNetworkNumeric(String networkNumeric) { + this.networkNumeric = networkNumeric; + } + + public void setPhoneNumbers(ArrayList phoneNumbers) { + this.phoneNumbers = phoneNumbers; + } + + public void addPhoneNumber(String phoneNumber) { + this.phoneNumbers.add(phoneNumber); + } + + public static SensitivePhoneNumber readSensitivePhoneNumbers (XmlPullParser parser) + throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, ns, "sensitivePN"); + + String numeric = parser.getAttributeValue(null, "network"); + + ArrayList numbers = null; + numbers = readPhoneNumber(parser); + + return new SensitivePhoneNumber(numeric, numbers); + } + + private static ArrayList readPhoneNumber (XmlPullParser parser) + throws XmlPullParserException, IOException { + ArrayList numbers = new ArrayList<>(); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + parser.require(XmlPullParser.START_TAG, ns, "item"); + + String item = ""; + if (parser.next() == XmlPullParser.TEXT) { + item = parser.getText(); + parser.nextTag(); + } + parser.require(XmlPullParser.END_TAG, ns, "item"); + + numbers.add(item); + } + return numbers; + } +} diff --git a/src/com/android/server/telecom/SensitivePhoneNumbers.java b/src/com/android/server/telecom/SensitivePhoneNumbers.java new file mode 100644 index 00000000..e652bc04 --- /dev/null +++ b/src/com/android/server/telecom/SensitivePhoneNumbers.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2017 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.telecom; + +import android.content.Context; +import android.os.Environment; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +public class SensitivePhoneNumbers { + private final String LOG_TAG = this.getClass().getSimpleName(); + + public static final String SENSIBLE_PHONENUMBERS_FILE_PATH = "etc/sensitive_pn.xml"; + private static final String ns = null; + + private HashMap> mSensitiveNumbersMap = new HashMap<>(); + + public SensitivePhoneNumbers() { + loadSensiblePhoneNumbers(); + } + + private void loadSensiblePhoneNumbers() { + FileReader sensiblePNReader; + + File sensiblePNFile = new File(Environment.getRootDirectory(), + SENSIBLE_PHONENUMBERS_FILE_PATH); + + try { + sensiblePNReader = new FileReader(sensiblePNFile); + } catch (FileNotFoundException e) { + Log.w(LOG_TAG, "Can not open " + sensiblePNFile.getAbsolutePath()); + return; + } + + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(sensiblePNReader); + parser.nextTag(); + + readSensitivePNS(parser); + + sensiblePNReader.close(); + } catch (XmlPullParserException e) { + Log.w(LOG_TAG, "Exception in spn-conf parser " + e); + } catch (IOException e) { + Log.w(LOG_TAG, "Exception in spn-conf parser " + e); + } + } + + private void readSensitivePNS(XmlPullParser parser) + throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, ns, "sensitivePNS"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if ("sensitivePN".equals(name)) { + SensitivePhoneNumber sensitivePN = SensitivePhoneNumber + .readSensitivePhoneNumbers(parser); + mSensitiveNumbersMap.put(sensitivePN.getNetworkNumeric(), + sensitivePN.getPhoneNumbers()); + } else { + break; + } + } + } + + public boolean isSensitiveNumber(Context context, String numberToCheck, String subId) { + TelephonyManager telephonyManager = + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + + int subIdInt = SubscriptionManager.getDefaultSubscriptionId(); + try{ + subIdInt = Integer.valueOf(subId); + }catch (NumberFormatException e) { + Log.w(LOG_TAG, "Error parsing subId"); + } + + String networkUsed = telephonyManager.getNetworkOperator(subIdInt); + if (!TextUtils.isEmpty(networkUsed)) { + String networkMCC = networkUsed.substring(0, 3); + return mSensitiveNumbersMap.containsKey(networkMCC) && + mSensitiveNumbersMap.get(networkMCC).contains(numberToCheck); + } + return false; + } +} diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java index 3bf044ce..36d1f7a8 100644 --- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java +++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java @@ -27,6 +27,7 @@ import android.location.Country; import android.location.CountryDetector; import android.location.CountryListener; import android.net.Uri; +import android.os.Environment; import android.os.Looper; import android.os.PersistableBundle; import android.os.UserHandle; @@ -40,15 +41,18 @@ import android.telecom.PhoneAccountHandle; import android.telecom.VideoProfile; import android.telephony.CarrierConfigManager; import android.telephony.PhoneNumberUtils; +import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import com.android.server.telecom.Call; import com.android.server.telecom.CallLogManager; import com.android.server.telecom.CallState; +import com.android.server.telecom.CallsManager; import com.android.server.telecom.MissedCallNotifier; import com.android.server.telecom.PhoneAccountRegistrar; import com.android.server.telecom.R; +import com.android.server.telecom.SensitivePhoneNumbers; import com.android.server.telecom.TelephonyUtil; import static org.mockito.Matchers.any; @@ -57,15 +61,18 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.io.File; import java.util.Arrays; import java.util.Locale; @@ -78,6 +85,7 @@ public class CallLogManagerTest extends TelecomTestCase { private PhoneAccountHandle mManagedProfileAccountHandle; private static final Uri TEL_PHONEHANDLE = Uri.parse("tel:5555551234"); + private static final Uri TEL_SENSITIVE_PHONEHANDLE = Uri.parse("tel:016"); private static final PhoneAccountHandle EMERGENCY_ACCT_HANDLE = TelephonyUtil .getDefaultEmergencyPhoneAccount() @@ -223,6 +231,32 @@ public class CallLogManagerTest extends TelecomTestCase { verifyNoInsertion(); } + @MediumTest + public void testDontLogCallsToSensitivePhoneNumber() { + when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class))) + .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID)); + + TelephonyManager mockTelephonyManager = + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + when(mockTelephonyManager.getNetworkOperator(Mockito.anyInt())).thenReturn("21407"); + + Call fakeCall = makeFakeCall( + DisconnectCause.OTHER, // disconnectCauseCode + false, // isConference + false, // isIncoming + 1L, // creationTimeMillis + 1000L, // ageMillis + TEL_SENSITIVE_PHONEHANDLE, // callHandle + mDefaultAccountHandle, // phoneAccountHandle + NO_VIDEO_STATE, // callVideoState + POST_DIAL_STRING, // postDialDigits + VIA_NUMBER_STRING, // viaNumber + UserHandle.of(CURRENT_USER_ID) + ); + mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED); + verifyNoInsertionIfFileExists(); + } + @MediumTest public void testLogCallDirectionOutgoing() { when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class))) @@ -679,6 +713,21 @@ public class CallLogManagerTest extends TelecomTestCase { } } + private void verifyNoInsertionIfFileExists() { + File sensiblePNFile = new File(Environment.getRootDirectory(), + SensitivePhoneNumbers.SENSIBLE_PHONENUMBERS_FILE_PATH); + try { + Thread.sleep(TEST_TIMEOUT_MILLIS); + verify(mContentProvider, + sensiblePNFile.exists() ? never() : atLeastOnce()).insert(any(String.class), + any(Uri.class), any(ContentValues.class)); + } catch (android.os.RemoteException e) { + fail("Remote exception occurred during test execution"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + private void verifyNoInsertionInUser(int userId) { try { -- cgit v1.2.3