blob: 83fd20803ed8d2b48906c3128508b782acd23941 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source 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.internal.telephony.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.AppOpsManager;
import android.app.role.RoleManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Telephony;
import android.telephony.TelephonyManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.telephony.SmsApplication;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Unit tests for the {@link SmsApplication} utility class
*/
@RunWith(AndroidJUnit4.class)
public class SmsApplicationTest {
private static final ComponentName TEST_COMPONENT_NAME =
ComponentName.unflattenFromString("com.android.test/.TestSmsApp");
private static final String MMS_RECEIVER_NAME = "TestMmsReceiver";
private static final String RESPOND_VIA_SMS_NAME = "TestRespondViaSmsHandler";
private static final String SEND_TO_NAME = "TestSendTo";
private static final int SMS_APP_UID = 10001;
private static final int FAKE_PHONE_UID = 10002;
private static final int FAKE_MMS_UID = 10003;
private static final int FAKE_BT_UID = 10004;
private static final int FAKE_TELEPHONY_PROVIDER_UID = 10005;
private static final String[] APP_OPS_TO_CHECK = {
AppOpsManager.OPSTR_READ_SMS,
AppOpsManager.OPSTR_WRITE_SMS,
AppOpsManager.OPSTR_RECEIVE_SMS,
AppOpsManager.OPSTR_RECEIVE_WAP_PUSH,
AppOpsManager.OPSTR_SEND_SMS,
AppOpsManager.OPSTR_READ_CELL_BROADCASTS
};
private static final Set<String> SCHEMES_FOR_PREFERRED_APP = Arrays.stream(new String[]{
"mms",
"mmsto",
"sms",
"smsto"
}).collect(Collectors.toSet());
@Mock private Context mContext;
@Mock private TelephonyManager mTelephonyManager;
@Mock private RoleManager mRoleManager;
@Mock private PackageManager mPackageManager;
@Mock private AppOpsManager mAppOpsManager;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
when(mContext.getSystemService(Context.ROLE_SERVICE)).thenReturn(mRoleManager);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager);
when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager);
when(mContext.createContextAsUser(isNotNull(), anyInt())).thenReturn(mContext);
doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
.when(mPackageManager)
.queryBroadcastReceiversAsUser(nullable(Intent.class), anyInt(),
nullable(UserHandle.class));
doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
.when(mPackageManager)
.queryIntentActivitiesAsUser(nullable(Intent.class), anyInt(),
nullable(UserHandle.class));
doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
.when(mPackageManager)
.queryIntentServicesAsUser(nullable(Intent.class), anyInt(),
nullable(UserHandle.class));
when(mTelephonyManager.isSmsCapable()).thenReturn(true);
when(mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)).thenReturn(true);
when(mRoleManager.getDefaultSmsPackage(anyInt()))
.thenReturn(TEST_COMPONENT_NAME.getPackageName());
for (String opStr : APP_OPS_TO_CHECK) {
when(mAppOpsManager.unsafeCheckOp(
opStr, SMS_APP_UID, TEST_COMPONENT_NAME.getPackageName()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
}
}
@Test
public void testGetDefaultSmsApplication() {
assertEquals(TEST_COMPONENT_NAME,
SmsApplication.getDefaultSmsApplicationAsUser(mContext, false, 0));
}
@Test
public void testGetDefaultSmsApplicationWithAppOpsFix() throws Exception {
when(mAppOpsManager.unsafeCheckOp(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID,
TEST_COMPONENT_NAME.getPackageName()))
.thenReturn(AppOpsManager.MODE_IGNORED);
setupPackageInfosForCoreApps();
assertEquals(TEST_COMPONENT_NAME,
SmsApplication.getDefaultSmsApplicationAsUser(mContext, true, 0));
verify(mAppOpsManager, atLeastOnce()).setUidMode(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID,
AppOpsManager.MODE_ALLOWED);
}
@Test
public void testPackageChanged() throws Exception {
setupPackageInfosForCoreApps();
SmsApplication.initSmsPackageMonitor(mContext);
verify(mContext).createContextAsUser(eq(UserHandle.ALL), anyInt());
ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mContext).registerReceiver(captor.capture(), isNotNull(),
isNull(), nullable(Handler.class));
BroadcastReceiver smsPackageMonitor = captor.getValue();
Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
packageChangedIntent.setData(
Uri.fromParts("package", TEST_COMPONENT_NAME.getPackageName(), null));
smsPackageMonitor.onReceive(mContext, packageChangedIntent);
ArgumentCaptor<IntentFilter> intentFilterCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
verify(mPackageManager, times(SCHEMES_FOR_PREFERRED_APP.size()))
.replacePreferredActivity(intentFilterCaptor.capture(),
eq(IntentFilter.MATCH_CATEGORY_SCHEME
| IntentFilter.MATCH_ADJUSTMENT_NORMAL),
isNotNull(List.class),
eq(new ComponentName(TEST_COMPONENT_NAME.getPackageName(), SEND_TO_NAME)));
Set<String> capturedSchemes = intentFilterCaptor.getAllValues().stream()
.map(intentFilter -> intentFilter.getDataScheme(0))
.collect(Collectors.toSet());
assertEquals(SCHEMES_FOR_PREFERRED_APP.size(), capturedSchemes.size());
assertTrue(SCHEMES_FOR_PREFERRED_APP.containsAll(capturedSchemes));
}
private void setupPackageInfosForCoreApps() throws Exception {
PackageInfo phonePackageInfo = new PackageInfo();
ApplicationInfo phoneApplicationInfo = new ApplicationInfo();
phoneApplicationInfo.uid = FAKE_PHONE_UID;
phonePackageInfo.applicationInfo = phoneApplicationInfo;
when(mPackageManager.getPackageInfo(eq(SmsApplication.PHONE_PACKAGE_NAME), anyInt()))
.thenReturn(phonePackageInfo);
PackageInfo mmsPackageInfo = new PackageInfo();
ApplicationInfo mmsApplicationInfo = new ApplicationInfo();
mmsApplicationInfo.uid = FAKE_MMS_UID;
mmsPackageInfo.applicationInfo = mmsApplicationInfo;
when(mPackageManager.getPackageInfo(eq(SmsApplication.MMS_SERVICE_PACKAGE_NAME), anyInt()))
.thenReturn(mmsPackageInfo);
PackageInfo bluetoothPackageInfo = new PackageInfo();
ApplicationInfo bluetoothApplicationInfo = new ApplicationInfo();
bluetoothApplicationInfo.uid = FAKE_BT_UID;
bluetoothPackageInfo.applicationInfo = bluetoothApplicationInfo;
when(mPackageManager.getPackageInfo(eq(SmsApplication.BLUETOOTH_PACKAGE_NAME), anyInt()))
.thenReturn(bluetoothPackageInfo);
PackageInfo telephonyProviderPackageInfo = new PackageInfo();
ApplicationInfo telephonyProviderApplicationInfo = new ApplicationInfo();
telephonyProviderApplicationInfo.uid = FAKE_TELEPHONY_PROVIDER_UID;
telephonyProviderPackageInfo.applicationInfo = telephonyProviderApplicationInfo;
when(mPackageManager.getPackageInfo(
eq(SmsApplication.TELEPHONY_PROVIDER_PACKAGE_NAME), anyInt()))
.thenReturn(telephonyProviderPackageInfo);
}
private List<ResolveInfo> getResolveInfosForIntent(Intent intent) {
switch (intent.getAction()) {
case Telephony.Sms.Intents.SMS_DELIVER_ACTION:
return Collections.singletonList(makeSmsDeliverResolveInfo());
case Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION:
return Collections.singletonList(makeWapPushResolveInfo());
case TelephonyManager.ACTION_RESPOND_VIA_MESSAGE:
return Collections.singletonList(makeRespondViaMessageResolveInfo());
case Intent.ACTION_SENDTO:
return Collections.singletonList(makeSendToResolveInfo());
}
return Collections.emptyList();
}
private ApplicationInfo makeSmsApplicationInfo() {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = SMS_APP_UID;
return applicationInfo;
}
private ResolveInfo makeSmsDeliverResolveInfo() {
ResolveInfo info = new ResolveInfo();
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.applicationInfo = makeSmsApplicationInfo();
activityInfo.permission = Manifest.permission.BROADCAST_SMS;
activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
activityInfo.name = TEST_COMPONENT_NAME.getClassName();
info.activityInfo = activityInfo;
return info;
}
private ResolveInfo makeWapPushResolveInfo() {
ResolveInfo info = new ResolveInfo();
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.permission = Manifest.permission.BROADCAST_WAP_PUSH;
activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
activityInfo.name = MMS_RECEIVER_NAME;
info.activityInfo = activityInfo;
return info;
}
private ResolveInfo makeRespondViaMessageResolveInfo() {
ResolveInfo info = new ResolveInfo();
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.permission = Manifest.permission.SEND_RESPOND_VIA_MESSAGE;
serviceInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
serviceInfo.name = RESPOND_VIA_SMS_NAME;
info.serviceInfo = serviceInfo;
return info;
}
private ResolveInfo makeSendToResolveInfo() {
ResolveInfo info = new ResolveInfo();
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
activityInfo.name = SEND_TO_NAME;
info.activityInfo = activityInfo;
return info;
}
}