diff options
author | Hall Liu <hallliu@google.com> | 2020-01-03 16:38:19 -0800 |
---|---|---|
committer | Hall Liu <hallliu@google.com> | 2020-01-06 18:21:32 -0800 |
commit | 070380947280e2dac930f9504ffc288a718c70b5 (patch) | |
tree | 281f4c3928d388efe799d4ec142c675f71deeaa2 | |
parent | 5edd9b25418d4c7e109ed3502733766e5472a1dc (diff) |
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
5 files changed, 112 insertions, 44 deletions
diff --git a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java index d4d07ea8e54c..0b47547d3b0c 100644 --- a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java +++ b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java @@ -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 */ diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java index 0275cb98eda9..b30258906368 100644 --- a/telephony/common/com/android/internal/telephony/SmsApplication.java +++ b/telephony/common/com/android/internal/telephony/SmsApplication.java @@ -58,6 +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; /** * Class for managing the primary application that we will deliver SMS/MMS messages to @@ -248,6 +249,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); @@ -256,7 +258,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>(); @@ -283,7 +285,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) { @@ -325,7 +327,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) { @@ -343,7 +345,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); @@ -370,7 +372,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); @@ -397,7 +399,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); @@ -626,7 +628,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 ? @@ -637,8 +640,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); @@ -802,9 +804,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, @@ -815,10 +824,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); } } } @@ -830,41 +840,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); } /** diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp index 16189c8e0ac8..61c3829e68a1 100644 --- a/tests/TelephonyCommonTests/Android.bp +++ b/tests/TelephonyCommonTests/Android.bp @@ -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", diff --git a/tests/TelephonyCommonTests/jarjar-rules.txt b/tests/TelephonyCommonTests/jarjar-rules.txt index fe3471973c6e..4d1115f3c56c 100644 --- a/tests/TelephonyCommonTests/jarjar-rules.txt +++ b/tests/TelephonyCommonTests/jarjar-rules.txt @@ -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 diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java index 6d0ee1891ac9..83fd20803ed8 100644 --- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java +++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java @@ -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(); |