Merge changes from topic "fix-packagechangercvr"
* changes:
Fix usages of PackageManager APIs in Telephony
Fix hidden API usage in PackageChangeReceiver
diff --git a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
index 922af12..0b47547 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.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,28 +43,24 @@
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) {
- if (user != null) {
- context.registerReceiverAsUser(this, user, sPackageIntentFilter, null, handler);
- } else {
- context.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);
}
/**
@@ -78,6 +74,14 @@
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 0275cb9..b302589 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.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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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 @@
}
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 @@
}
}
}
+ 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 @@
@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 ae647eb..4f7569d 100644
--- a/tests/TelephonyCommonTests/Android.bp
+++ b/tests/TelephonyCommonTests/Android.bp
@@ -38,7 +38,7 @@
// 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 fe34719..4d1115f 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 6d0ee18..83fd208 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 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 @@
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 @@
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 @@
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();