Loading src/com/android/settings/applications/ApplicationFeatureProviderImpl.java +98 −2 Original line number Diff line number Diff line Loading @@ -16,24 +16,30 @@ package com.android.settings.applications; import android.Manifest; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ComponentInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.location.LocationManager; import android.os.RemoteException; import android.os.UserManager; import android.service.euicc.EuiccService; import android.telecom.DefaultDialerManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.internal.telephony.SmsApplication; import com.android.internal.telephony.euicc.EuiccConnector; import com.android.settings.R; import java.util.ArrayList; Loading @@ -41,12 +47,17 @@ import java.util.List; import java.util.Set; public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvider { private static final String TAG = "AppFeatureProviderImpl"; protected final Context mContext; private final PackageManager mPm; private final IPackageManager mPms; private final DevicePolicyManager mDpm; private final UserManager mUm; /** Flags to use when querying PackageManager for Euicc component implementations. */ private static final int EUICC_QUERY_FLAGS = PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.GET_RESOLVED_FILTER; public ApplicationFeatureProviderImpl(Context context, PackageManager pm, IPackageManager pms, DevicePolicyManager dpm) { Loading Loading @@ -143,7 +154,7 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide } // Keep Euicc Service enabled. final ComponentInfo euicc = EuiccConnector.findBestComponent(mPm); final ComponentInfo euicc = findEuiccService(mPm); if (euicc != null) { keepEnabledPackages.add(euicc.packageName); } Loading Loading @@ -239,4 +250,89 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide mCallback.onListOfAppsResult(list); } } /** * Return the component info of the EuiccService to bind to, or null if none were found. */ @VisibleForTesting ComponentInfo findEuiccService(PackageManager packageManager) { final Intent intent = new Intent(EuiccService.EUICC_SERVICE_INTERFACE); final List<ResolveInfo> resolveInfoList = packageManager.queryIntentServices(intent, EUICC_QUERY_FLAGS); final ComponentInfo bestComponent = findEuiccService(packageManager, resolveInfoList); if (bestComponent == null) { Log.w(TAG, "No valid EuiccService implementation found"); } return bestComponent; } private ComponentInfo findEuiccService( PackageManager packageManager, List<ResolveInfo> resolveInfoList) { int bestPriority = Integer.MIN_VALUE; ComponentInfo bestComponent = null; if (resolveInfoList != null) { for (ResolveInfo resolveInfo : resolveInfoList) { if (!isValidEuiccComponent(packageManager, resolveInfo)) { continue; } if (resolveInfo.filter.getPriority() > bestPriority) { bestPriority = resolveInfo.filter.getPriority(); bestComponent = getComponentInfo(resolveInfo); } } } return bestComponent; } private boolean isValidEuiccComponent( PackageManager packageManager, ResolveInfo resolveInfo) { final ComponentInfo componentInfo = getComponentInfo(resolveInfo); final String packageName = componentInfo.packageName; // Verify that the app is privileged (via granting of a privileged permission). if (packageManager.checkPermission( Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, packageName) != PackageManager.PERMISSION_GRANTED) { Log.e(TAG, "Package " + packageName + " does not declare WRITE_EMBEDDED_SUBSCRIPTIONS"); return false; } // Verify that only the system can access the component. final String permission; if (componentInfo instanceof ServiceInfo) { permission = ((ServiceInfo) componentInfo).permission; } else if (componentInfo instanceof ActivityInfo) { permission = ((ActivityInfo) componentInfo).permission; } else { throw new IllegalArgumentException("Can only verify services/activities"); } if (!TextUtils.equals(permission, Manifest.permission.BIND_EUICC_SERVICE)) { Log.e(TAG, "Package " + packageName + " does not require the BIND_EUICC_SERVICE permission"); return false; } // Verify that the component declares a priority. if (resolveInfo.filter == null || resolveInfo.filter.getPriority() == 0) { Log.e(TAG, "Package " + packageName + " does not specify a priority"); return false; } return true; } private ComponentInfo getComponentInfo(ResolveInfo resolveInfo) { if (resolveInfo.activityInfo != null) { return resolveInfo.activityInfo; } if (resolveInfo.serviceInfo != null) { return resolveInfo.serviceInfo; } if (resolveInfo.providerInfo != null) { return resolveInfo.providerInfo; } throw new IllegalStateException("Missing ComponentInfo!"); } } tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java +7 −33 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.settings.applications; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; Loading @@ -37,12 +38,10 @@ import android.os.Build; import android.os.UserHandle; import android.os.UserManager; import com.android.internal.telephony.euicc.EuiccConnector; import com.android.settings.testutils.ApplicationTestUtils; import com.android.settingslib.testutils.shadow.ShadowDefaultDialerManager; import com.android.settingslib.testutils.shadow.ShadowSmsApplication; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -51,8 +50,6 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowApplication; import org.robolectric.util.ReflectionHelpers; Loading Loading @@ -109,12 +106,6 @@ public final class ApplicationFeatureProviderImplTest { mPackageManagerService, mDevicePolicyManager); } @After @Config(shadows = {ShadowEuiccConnector.class}) public void tearDown() { ShadowEuiccConnector.reset(); } private void verifyCalculateNumberOfPolicyInstalledApps(boolean async) { setUpUsersAndInstalledApps(); Loading Loading @@ -293,8 +284,7 @@ public final class ApplicationFeatureProviderImplTest { } @Test @Config(shadows = {ShadowSmsApplication.class, ShadowDefaultDialerManager.class, ShadowEuiccConnector.class}) @Config(shadows = {ShadowSmsApplication.class, ShadowDefaultDialerManager.class}) public void getKeepEnabledPackages_hasEuiccComponent_shouldContainEuiccPackage() { final String testDialer = "com.android.test.defaultdialer"; final String testSms = "com.android.test.defaultsms"; Loading @@ -305,7 +295,10 @@ public final class ApplicationFeatureProviderImplTest { ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer); final ComponentInfo componentInfo = new ComponentInfo(); componentInfo.packageName = testEuicc; ShadowEuiccConnector.setBestComponent(componentInfo); ApplicationFeatureProviderImpl spyProvider = spy(new ApplicationFeatureProviderImpl( mContext, mPackageManager, mPackageManagerService, mDevicePolicyManager)); doReturn(componentInfo).when(spyProvider).findEuiccService(mPackageManager); // Spy the real context to mock LocationManager. Context spyContext = spy(RuntimeEnvironment.application); Loading @@ -314,7 +307,7 @@ public final class ApplicationFeatureProviderImplTest { ReflectionHelpers.setField(mProvider, "mContext", spyContext); final Set<String> keepEnabledPackages = mProvider.getKeepEnabledPackages(); final Set<String> keepEnabledPackages = spyProvider.getKeepEnabledPackages(); assertThat(keepEnabledPackages).contains(testEuicc); } Loading Loading @@ -391,23 +384,4 @@ public final class ApplicationFeatureProviderImplTest { resolveInfo.activityInfo = activityInfo; return resolveInfo; } @Implements(EuiccConnector.class) public static class ShadowEuiccConnector { private static ComponentInfo sBestComponent; @Implementation protected static ComponentInfo findBestComponent(PackageManager packageManager) { return sBestComponent; } public static void setBestComponent(ComponentInfo componentInfo) { sBestComponent = componentInfo; } public static void reset() { sBestComponent = null; } } } Loading
src/com/android/settings/applications/ApplicationFeatureProviderImpl.java +98 −2 Original line number Diff line number Diff line Loading @@ -16,24 +16,30 @@ package com.android.settings.applications; import android.Manifest; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ComponentInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.location.LocationManager; import android.os.RemoteException; import android.os.UserManager; import android.service.euicc.EuiccService; import android.telecom.DefaultDialerManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.internal.telephony.SmsApplication; import com.android.internal.telephony.euicc.EuiccConnector; import com.android.settings.R; import java.util.ArrayList; Loading @@ -41,12 +47,17 @@ import java.util.List; import java.util.Set; public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvider { private static final String TAG = "AppFeatureProviderImpl"; protected final Context mContext; private final PackageManager mPm; private final IPackageManager mPms; private final DevicePolicyManager mDpm; private final UserManager mUm; /** Flags to use when querying PackageManager for Euicc component implementations. */ private static final int EUICC_QUERY_FLAGS = PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.GET_RESOLVED_FILTER; public ApplicationFeatureProviderImpl(Context context, PackageManager pm, IPackageManager pms, DevicePolicyManager dpm) { Loading Loading @@ -143,7 +154,7 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide } // Keep Euicc Service enabled. final ComponentInfo euicc = EuiccConnector.findBestComponent(mPm); final ComponentInfo euicc = findEuiccService(mPm); if (euicc != null) { keepEnabledPackages.add(euicc.packageName); } Loading Loading @@ -239,4 +250,89 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide mCallback.onListOfAppsResult(list); } } /** * Return the component info of the EuiccService to bind to, or null if none were found. */ @VisibleForTesting ComponentInfo findEuiccService(PackageManager packageManager) { final Intent intent = new Intent(EuiccService.EUICC_SERVICE_INTERFACE); final List<ResolveInfo> resolveInfoList = packageManager.queryIntentServices(intent, EUICC_QUERY_FLAGS); final ComponentInfo bestComponent = findEuiccService(packageManager, resolveInfoList); if (bestComponent == null) { Log.w(TAG, "No valid EuiccService implementation found"); } return bestComponent; } private ComponentInfo findEuiccService( PackageManager packageManager, List<ResolveInfo> resolveInfoList) { int bestPriority = Integer.MIN_VALUE; ComponentInfo bestComponent = null; if (resolveInfoList != null) { for (ResolveInfo resolveInfo : resolveInfoList) { if (!isValidEuiccComponent(packageManager, resolveInfo)) { continue; } if (resolveInfo.filter.getPriority() > bestPriority) { bestPriority = resolveInfo.filter.getPriority(); bestComponent = getComponentInfo(resolveInfo); } } } return bestComponent; } private boolean isValidEuiccComponent( PackageManager packageManager, ResolveInfo resolveInfo) { final ComponentInfo componentInfo = getComponentInfo(resolveInfo); final String packageName = componentInfo.packageName; // Verify that the app is privileged (via granting of a privileged permission). if (packageManager.checkPermission( Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, packageName) != PackageManager.PERMISSION_GRANTED) { Log.e(TAG, "Package " + packageName + " does not declare WRITE_EMBEDDED_SUBSCRIPTIONS"); return false; } // Verify that only the system can access the component. final String permission; if (componentInfo instanceof ServiceInfo) { permission = ((ServiceInfo) componentInfo).permission; } else if (componentInfo instanceof ActivityInfo) { permission = ((ActivityInfo) componentInfo).permission; } else { throw new IllegalArgumentException("Can only verify services/activities"); } if (!TextUtils.equals(permission, Manifest.permission.BIND_EUICC_SERVICE)) { Log.e(TAG, "Package " + packageName + " does not require the BIND_EUICC_SERVICE permission"); return false; } // Verify that the component declares a priority. if (resolveInfo.filter == null || resolveInfo.filter.getPriority() == 0) { Log.e(TAG, "Package " + packageName + " does not specify a priority"); return false; } return true; } private ComponentInfo getComponentInfo(ResolveInfo resolveInfo) { if (resolveInfo.activityInfo != null) { return resolveInfo.activityInfo; } if (resolveInfo.serviceInfo != null) { return resolveInfo.serviceInfo; } if (resolveInfo.providerInfo != null) { return resolveInfo.providerInfo; } throw new IllegalStateException("Missing ComponentInfo!"); } }
tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java +7 −33 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.settings.applications; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; Loading @@ -37,12 +38,10 @@ import android.os.Build; import android.os.UserHandle; import android.os.UserManager; import com.android.internal.telephony.euicc.EuiccConnector; import com.android.settings.testutils.ApplicationTestUtils; import com.android.settingslib.testutils.shadow.ShadowDefaultDialerManager; import com.android.settingslib.testutils.shadow.ShadowSmsApplication; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -51,8 +50,6 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowApplication; import org.robolectric.util.ReflectionHelpers; Loading Loading @@ -109,12 +106,6 @@ public final class ApplicationFeatureProviderImplTest { mPackageManagerService, mDevicePolicyManager); } @After @Config(shadows = {ShadowEuiccConnector.class}) public void tearDown() { ShadowEuiccConnector.reset(); } private void verifyCalculateNumberOfPolicyInstalledApps(boolean async) { setUpUsersAndInstalledApps(); Loading Loading @@ -293,8 +284,7 @@ public final class ApplicationFeatureProviderImplTest { } @Test @Config(shadows = {ShadowSmsApplication.class, ShadowDefaultDialerManager.class, ShadowEuiccConnector.class}) @Config(shadows = {ShadowSmsApplication.class, ShadowDefaultDialerManager.class}) public void getKeepEnabledPackages_hasEuiccComponent_shouldContainEuiccPackage() { final String testDialer = "com.android.test.defaultdialer"; final String testSms = "com.android.test.defaultsms"; Loading @@ -305,7 +295,10 @@ public final class ApplicationFeatureProviderImplTest { ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer); final ComponentInfo componentInfo = new ComponentInfo(); componentInfo.packageName = testEuicc; ShadowEuiccConnector.setBestComponent(componentInfo); ApplicationFeatureProviderImpl spyProvider = spy(new ApplicationFeatureProviderImpl( mContext, mPackageManager, mPackageManagerService, mDevicePolicyManager)); doReturn(componentInfo).when(spyProvider).findEuiccService(mPackageManager); // Spy the real context to mock LocationManager. Context spyContext = spy(RuntimeEnvironment.application); Loading @@ -314,7 +307,7 @@ public final class ApplicationFeatureProviderImplTest { ReflectionHelpers.setField(mProvider, "mContext", spyContext); final Set<String> keepEnabledPackages = mProvider.getKeepEnabledPackages(); final Set<String> keepEnabledPackages = spyProvider.getKeepEnabledPackages(); assertThat(keepEnabledPackages).contains(testEuicc); } Loading Loading @@ -391,23 +384,4 @@ public final class ApplicationFeatureProviderImplTest { resolveInfo.activityInfo = activityInfo; return resolveInfo; } @Implements(EuiccConnector.class) public static class ShadowEuiccConnector { private static ComponentInfo sBestComponent; @Implementation protected static ComponentInfo findBestComponent(PackageManager packageManager) { return sBestComponent; } public static void setBestComponent(ComponentInfo componentInfo) { sBestComponent = componentInfo; } public static void reset() { sBestComponent = null; } } }