Fix usages of PackageManager APIs in Telephony

Remove usages of the hidden PackageManager#*asUser methods from
Telephony, and add some unit tests to verify the functionality.

Test: atest FrameworksTelephonyTests
Test: atest TelephonyCommonTests
Bug: 146834818
Change-Id: I2e2ec2c421773e3ff58556373896238e1ff0678a
Merged-In: I2e2ec2c421773e3ff58556373896238e1ff0678a
This commit is contained in:
Hall Liu
2020-01-03 16:38:19 -08:00
parent 437010229d
commit e7baa9e91e
5 changed files with 112 additions and 45 deletions

View File

@@ -24,17 +24,17 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
import com.android.internal.os.BackgroundThread;
/**
* Helper class for monitoring the state of packages: adding, removing,
* updating, and disappearing and reappearing on the SD card.
*/
public abstract class PackageChangeReceiver extends BroadcastReceiver {
static final IntentFilter sPackageIntentFilter = new IntentFilter();
private static HandlerThread sHandlerThread;
static {
sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -43,24 +43,24 @@ public abstract class PackageChangeReceiver extends BroadcastReceiver {
sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
sPackageIntentFilter.addDataScheme("package");
}
// Keep an instance of Context around as long as we still want the receiver:
// if the instance of Context gets garbage-collected, it'll unregister the receiver, so only
// unset when we want to unregister.
Context mRegisteredContext;
/**
* To register the intents that needed for monitoring the state of packages
* To register the intents that needed for monitoring the state of packages. Once this method
* has been called on an instance of {@link PackageChangeReceiver}, all subsequent calls must
* have the same {@code user} argument.
*/
public void register(@NonNull Context context, @Nullable Looper thread,
@Nullable UserHandle user) {
if (mRegisteredContext != null) {
throw new IllegalStateException("Already registered");
}
Handler handler = (thread == null) ? BackgroundThread.getHandler() : new Handler(thread);
mRegisteredContext = context;
if (handler != null) {
Context contextForUser = user == null ? context : context.createContextAsUser(user, 0);
contextForUser.registerReceiver(this, sPackageIntentFilter, null, handler);
} else {
throw new NullPointerException();
}
Handler handler = new Handler(thread == null ? getStaticLooper() : thread);
mRegisteredContext = user == null ? context : context.createContextAsUser(user, 0);
mRegisteredContext.registerReceiver(this, sPackageIntentFilter, null, handler);
}
/**
@@ -74,6 +74,14 @@ public abstract class PackageChangeReceiver extends BroadcastReceiver {
mRegisteredContext = null;
}
private static synchronized Looper getStaticLooper() {
if (sHandlerThread == null) {
sHandlerThread = new HandlerThread(PackageChangeReceiver.class.getSimpleName());
sHandlerThread.start();
}
return sHandlerThread.getLooper();
}
/**
* This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED
*/

View File

@@ -58,7 +58,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
@@ -250,6 +250,7 @@ public final class SmsApplication {
private static Collection<SmsApplicationData> getApplicationCollectionInternal(
Context context, int userId) {
PackageManager packageManager = context.getPackageManager();
UserHandle userHandle = UserHandle.of(userId);
// Get the list of apps registered for SMS
Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
@@ -258,7 +259,7 @@ public final class SmsApplication {
}
List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
userHandle);
HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
@@ -285,7 +286,7 @@ public final class SmsApplication {
intent.setDataAndType(null, "application/vnd.wap.mms-message");
List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
userHandle);
for (ResolveInfo resolveInfo : mmsReceivers) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
if (activityInfo == null) {
@@ -327,7 +328,7 @@ public final class SmsApplication {
Uri.fromParts(SCHEME_SMSTO, "", null));
List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
userHandle);
for (ResolveInfo resolveInfo : sendToActivities) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
if (activityInfo == null) {
@@ -345,7 +346,7 @@ public final class SmsApplication {
List<ResolveInfo> smsAppChangedReceivers =
packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" +
smsAppChangedReceivers);
@@ -372,7 +373,7 @@ public final class SmsApplication {
List<ResolveInfo> providerChangedReceivers =
packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" +
providerChangedReceivers);
@@ -399,7 +400,7 @@ public final class SmsApplication {
List<ResolveInfo> simFullReceivers =
packageManager.queryBroadcastReceiversAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal simFullReceivers="
+ simFullReceivers);
@@ -631,7 +632,8 @@ public final class SmsApplication {
}
// We only make the change if the new package is valid
PackageManager packageManager = context.getPackageManager();
PackageManager packageManager =
context.createContextAsUser(userHandle, 0).getPackageManager();
Collection<SmsApplicationData> applications = getApplicationCollectionInternal(
context, userId);
SmsApplicationData oldAppData = oldPackageName != null ?
@@ -642,8 +644,7 @@ public final class SmsApplication {
AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
if (oldPackageName != null) {
try {
int uid = packageManager.getPackageInfoAsUser(
oldPackageName, 0, userId).applicationInfo.uid;
int uid = packageManager.getPackageInfo(oldPackageName, 0).applicationInfo.uid;
setExclusiveAppops(oldPackageName, appOps, uid, AppOpsManager.MODE_DEFAULT);
} catch (NameNotFoundException e) {
Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
@@ -807,9 +808,16 @@ public final class SmsApplication {
}
private void onPackageChanged() {
PackageManager packageManager = mContext.getPackageManager();
int userId;
try {
userId = getSendingUser().getIdentifier();
} catch (NullPointerException e) {
// This should never happen in prod -- unit tests will put the receiver into a
// unusual state where the pending result is null, which produces a NPE when calling
// getSendingUserId. Just pretend like it's the system user for testing.
userId = UserHandle.USER_SYSTEM;
}
Context userContext = mContext;
final int userId = getSendingUserId();
if (userId != UserHandle.USER_SYSTEM) {
try {
userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
@@ -820,10 +828,11 @@ public final class SmsApplication {
}
}
}
PackageManager packageManager = userContext.getPackageManager();
// Ensure this component is still configured as the preferred activity
ComponentName componentName = getDefaultSendToApplication(userContext, true);
if (componentName != null) {
configurePreferredActivity(packageManager, componentName, userId);
configurePreferredActivity(packageManager, componentName);
}
}
}
@@ -835,41 +844,36 @@ public final class SmsApplication {
@UnsupportedAppUsage
private static void configurePreferredActivity(PackageManager packageManager,
ComponentName componentName, int userId) {
ComponentName componentName) {
// Add the four activity preferences we want to direct to this app.
replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMS);
replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMSTO);
replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMS);
replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMSTO);
replacePreferredActivity(packageManager, componentName, SCHEME_SMS);
replacePreferredActivity(packageManager, componentName, SCHEME_SMSTO);
replacePreferredActivity(packageManager, componentName, SCHEME_MMS);
replacePreferredActivity(packageManager, componentName, SCHEME_MMSTO);
}
/**
* Updates the ACTION_SENDTO activity to the specified component for the specified scheme.
*/
private static void replacePreferredActivity(PackageManager packageManager,
ComponentName componentName, int userId, String scheme) {
ComponentName componentName, String scheme) {
// Build the set of existing activities that handle this scheme
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(scheme, "", null));
List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivitiesAsUser(
intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER,
userId);
List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(
intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER);
// Build the set of ComponentNames for these activities
final int n = resolveInfoList.size();
ComponentName[] set = new ComponentName[n];
for (int i = 0; i < n; i++) {
ResolveInfo info = resolveInfoList.get(i);
set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
}
List<ComponentName> components = resolveInfoList.stream().map(info ->
new ComponentName(info.activityInfo.packageName, info.activityInfo.name))
.collect(Collectors.toList());
// Update the preferred SENDTO activity for the specified scheme
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SENDTO);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
intentFilter.addDataScheme(scheme);
packageManager.replacePreferredActivityAsUser(intentFilter,
packageManager.replacePreferredActivity(intentFilter,
IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
set, componentName, userId);
components, componentName);
}
/**

View File

@@ -38,7 +38,7 @@ android_test {
// Uncomment this and comment out the jarjar rule if you want to attach a debugger and step
// through the renamed classes.
//platform_apis: true,
// platform_apis: true,
libs: [
"android.test.runner",

View File

@@ -1 +1,3 @@
rule com.android.internal.telephony.SmsApplication* com.android.internal.telephony.tests.SmsApplication@1
rule android.telephony.PackageChangeReceiver* com.android.internal.telephony.tests.PackageChangeReceiver@1
rule com.android.internal.os.BackgroundThread* com.android.internal.telephony.tests.BackgroundThread@1

View File

@@ -17,26 +17,34 @@
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;
@@ -48,11 +56,15 @@ 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
@@ -80,6 +92,13 @@ public class SmsApplicationTest {
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;
@@ -94,13 +113,16 @@ public class SmsApplicationTest {
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(), anyInt());
.queryBroadcastReceiversAsUser(nullable(Intent.class), anyInt(),
nullable(UserHandle.class));
doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
.when(mPackageManager)
.queryIntentActivitiesAsUser(nullable(Intent.class), anyInt(), anyInt());
.queryIntentActivitiesAsUser(nullable(Intent.class), anyInt(),
nullable(UserHandle.class));
doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
.when(mPackageManager)
.queryIntentServicesAsUser(nullable(Intent.class), anyInt(),
@@ -137,6 +159,37 @@ public class SmsApplicationTest {
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();