Merge "Clean up SmsApplication and expose getDefaultSmsPackage"

am: 2ada7fbac2

Change-Id: Ie4ca7c239295ba9e3bfb60815ca5c7d65bf0fdc1
This commit is contained in:
Hall Liu
2020-01-06 16:45:17 -08:00
committed by android-build-merger
8 changed files with 374 additions and 21 deletions

View File

@@ -1162,6 +1162,7 @@ package android.app.role {
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Nullable public String getDefaultSmsPackage(int);
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);

View File

@@ -629,9 +629,12 @@ public final class RoleManager {
* {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by
* {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}
*
* @param userId The user ID to get the default SMS package for.
* @return the package name of the default SMS app, or {@code null} if not configured.
* @hide
*/
@Nullable
@SystemApi
public String getDefaultSmsPackage(@UserIdInt int userId) {
try {
return mService.getDefaultSmsPackage(userId);

View File

@@ -44,6 +44,7 @@ import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -67,10 +68,10 @@ import java.util.function.Consumer;
*/
public final class SmsApplication {
static final String LOG_TAG = "SmsApplication";
private static final String PHONE_PACKAGE_NAME = "com.android.phone";
private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
private static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
public static final String PHONE_PACKAGE_NAME = "com.android.phone";
public static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
public static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
public static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
private static final String SCHEME_SMS = "sms";
private static final String SCHEME_SMSTO = "smsto";
@@ -79,13 +80,13 @@ public final class SmsApplication {
private static final boolean DEBUG = false;
private static final boolean DEBUG_MULTIUSER = false;
private static final int[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
AppOpsManager.OP_READ_SMS,
AppOpsManager.OP_WRITE_SMS,
AppOpsManager.OP_RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_WAP_PUSH,
AppOpsManager.OP_SEND_SMS,
AppOpsManager.OP_READ_CELL_BROADCASTS
private static final String[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
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 SmsPackageMonitor sSmsPackageMonitor = null;
@@ -501,7 +502,7 @@ public final class SmsApplication {
// If we found a package, make sure AppOps permissions are set up correctly
if (applicationData != null) {
// We can only call checkOp if we are privileged (updateIfNeeded) or if the app we
// We can only call unsafeCheckOp if we are privileged (updateIfNeeded) or if the app we
// are checking is for our current uid. Doing this check from the unprivileged current
// SMS app allows us to tell the current SMS app that it is not in a good state and
// needs to ask to be the current SMS app again to work properly.
@@ -555,23 +556,23 @@ public final class SmsApplication {
// apps, all of them should be able to write to telephony provider.
// This is to allow the proxy package permission check in telephony provider
// to pass.
for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
appOps.setUidMode(appop, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED);
for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
appOps.setUidMode(opStr, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED);
}
}
private static boolean tryFixExclusiveSmsAppops(Context context,
SmsApplicationData applicationData, boolean updateIfNeeded) {
AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
for (int appOp : DEFAULT_APP_EXCLUSIVE_APPOPS) {
int mode = appOps.checkOp(appOp, applicationData.mUid,
for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
int mode = appOps.unsafeCheckOp(opStr, applicationData.mUid,
applicationData.mPackageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
Rlog.e(LOG_TAG, applicationData.mPackageName + " lost "
+ AppOpsManager.modeToName(appOp) + ": "
+ opStr + ": "
+ (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
if (updateIfNeeded) {
appOps.setUidMode(appOp, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
appOps.setUidMode(opStr, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
} else {
return false;
}
@@ -757,7 +758,7 @@ public final class SmsApplication {
}
try {
PackageInfo info = packageManager.getPackageInfo(packageName, 0);
int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
int mode = appOps.unsafeCheckOp(AppOpsManager.OPSTR_WRITE_SMS, info.applicationInfo.uid,
packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS: (fixing)");
@@ -773,8 +774,8 @@ public final class SmsApplication {
private static void setExclusiveAppops(String pkg, AppOpsManager appOpsManager, int uid,
int mode) {
for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
appOpsManager.setUidMode(appop, uid, mode);
for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
appOpsManager.setUidMode(opStr, uid, mode);
}
}
@@ -899,6 +900,7 @@ public final class SmsApplication {
* @param userId target user ID.
* @return component name of the app and class to deliver SMS messages to
*/
@VisibleForTesting
public static ComponentName getDefaultSmsApplicationAsUser(Context context,
boolean updateIfNeeded, int userId) {
final long token = Binder.clearCallingIdentity();

View File

@@ -0,0 +1,48 @@
//
// 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.
//
android_test {
name: "TelephonyCommonTests",
srcs: [
":framework-telephony-common-sources",
"**/*.java",
],
static_libs: [
"mockito-target-extended",
"androidx.test.rules",
"truth-prebuilt",
"platform-test-annotations",
"androidx.core_core",
"androidx.fragment_fragment",
"androidx.test.ext.junit"
],
jni_libs: ["libdexmakerjvmtiagent"],
// We need to rename SmsApplication to the test package or else it'll get clobbered by the
// hidden api checker
jarjar_rules: "jarjar-rules.txt",
// Uncomment this and comment out the jarjar rule if you want to attach a debugger and step
// through the renamed classes.
//platform_apis: true,
libs: [
"android.test.runner",
"android.test.mock",
"android.test.base",
],
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.internal.telephony.tests"
android:debuggable="true">
<application android:label="TelephonyCommonTests"
android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.internal.telephony.tests"
android:label="Telephony common tests"
android:debuggable="true"/>
</manifest>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<configuration description="Runs Telephony Common Test Cases.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="TelephonyCommonTests.apk" />
</target_preparer>
<option name="test-tag" value="TelephonyCommonTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.internal.telephony.tests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
</configuration>

View File

@@ -0,0 +1 @@
rule com.android.internal.telephony.SmsApplication* com.android.internal.telephony.tests.SmsApplication@1

View File

@@ -0,0 +1,238 @@
/*
* 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.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
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.ComponentName;
import android.content.Context;
import android.content.Intent;
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.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.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.List;
/**
* 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
};
@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);
doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
.when(mPackageManager)
.queryBroadcastReceiversAsUser(nullable(Intent.class), anyInt(), anyInt());
doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
.when(mPackageManager)
.queryIntentActivitiesAsUser(nullable(Intent.class), anyInt(), anyInt());
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);
}
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;
}
}