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:
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user