diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 529c51d6bd..3b5f4030ab 100644 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -7799,6 +7799,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void onExternalApplicationsAvailable(@Nullable String[] pkgList) { mPermissionMonitor.onExternalApplicationsAvailable(pkgList); + if (mSatelliteAccessController != null) { + mSatelliteAccessController.onExternalApplicationsAvailable((pkgList)); + } } private void handlePackageChanged(@NonNull final String packageName) { diff --git a/service/src/com/android/server/connectivity/SatelliteAccessController.java b/service/src/com/android/server/connectivity/SatelliteAccessController.java index 50edacf6f8..7616ed78bb 100644 --- a/service/src/com/android/server/connectivity/SatelliteAccessController.java +++ b/service/src/com/android/server/connectivity/SatelliteAccessController.java @@ -31,12 +31,14 @@ import android.content.pm.PackageManager; import android.os.Handler; import android.os.UserHandle; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; +import com.android.net.module.util.CollectionUtils; import com.android.net.module.util.DeviceConfigUtils; import java.util.List; @@ -79,6 +81,8 @@ public class SatelliteAccessController { // accessed on the handler thread. private final Set mSatelliteDataOptimizedUids = new ArraySet<>(); + private final ArrayMap mUserPackageManagers = new ArrayMap<>(); + /** * Monitor {@link android.app.role.OnRoleHoldersChangedListener#onRoleHoldersChanged(String, * UserHandle)}, @@ -272,6 +276,11 @@ public class SatelliteAccessController { // Obtain uids with role sms and satellite communication permission for the added user. onRoleSmsChanged(userHandle); + // Store PackageManager for user for later use. + final PackageManager pmForUser = + mContext.createContextAsUser(userHandle, 0 /* flag */).getPackageManager(); + mUserPackageManagers.put(userHandle, pmForUser); + if (!mSupportConstrainedDataSatelliteOptIn) return; final Set satelliteDataOptimizedAppsForUser = @@ -293,6 +302,7 @@ public class SatelliteAccessController { final boolean smsRoleUidsChanged = updateSatelliteFallbackUidListOnUserRemoval(userHandle.getIdentifier()); final boolean mDataOptimizedUidChanged; + mUserPackageManagers.remove(userHandle); if (mSupportConstrainedDataSatelliteOptIn) { mDataOptimizedUidChanged = removeSatelliteDataOptimizedUidsForUser(userHandle.getIdentifier()); @@ -311,8 +321,43 @@ public class SatelliteAccessController { * @param uid The uid of the new package. */ public void onPackageAdded(@NonNull final String packageName, final int uid) { + if (!mSupportConstrainedDataSatelliteOptIn) return; + if (addSatelliteDataOptimizedUid(packageName, uid)) { + reportSatelliteNetworkFallbackUids(); + } + } + + @CheckReturnValue + private boolean addSatelliteDataOptimizedUid(@NonNull final String packageName, final int uid) { if (mSupportConstrainedDataSatelliteOptIn && isSatelliteDataOptimizedApp(packageName)) { mSatelliteDataOptimizedUids.add(uid); + return true; + } + return false; + } + + /** + * Called when the availability of external applications changes. + * + * @param pkgList An array of package names that have become available. + */ + public void onExternalApplicationsAvailable(String[] pkgList) { + if (!mSupportConstrainedDataSatelliteOptIn) return; + if (CollectionUtils.isEmpty(pkgList)) { + Log.e(TAG, "No available external application."); + return; + } + + boolean added = false; + for (String app : pkgList) { + for (final PackageManager pm : mUserPackageManagers.values()) { + final int uid = getUidForPackage(pm, app); + if (uid != INVALID_UID && addSatelliteDataOptimizedUid(app, uid)) { + added = true; + } + } + } + if (added) { reportSatelliteNetworkFallbackUids(); } } @@ -337,8 +382,10 @@ public class SatelliteAccessController { } // If the loop completes without returning, it means no other // satellite-optimized app shares the UID. - mSatelliteDataOptimizedUids.remove(uid); - reportSatelliteNetworkFallbackUids(); + final boolean removed = mSatelliteDataOptimizedUids.remove(uid); + if (removed) { + reportSatelliteNetworkFallbackUids(); + } } @NonNull diff --git a/tests/unit/java/com/android/server/connectivity/SatelliteAccessControllerTest.kt b/tests/unit/java/com/android/server/connectivity/SatelliteAccessControllerTest.kt index dc1aaca197..5df65eb06c 100644 --- a/tests/unit/java/com/android/server/connectivity/SatelliteAccessControllerTest.kt +++ b/tests/unit/java/com/android/server/connectivity/SatelliteAccessControllerTest.kt @@ -433,6 +433,7 @@ class SatelliteAccessControllerTest { .onUserAddedWithInstalledPackageList(PRIMARY_USER_HANDLE, listOf(packageInfo1)) mSatelliteAccessController.onPackageAdded(TEST_PACKAGE1, TEST_UID1) mSatelliteAccessController.onPackageRemoved(TEST_PACKAGE1, TEST_UID1) + mSatelliteAccessController.onExternalApplicationsAvailable(arrayOf(SMS_APP1, SMS_APP2)) mSatelliteAccessController.onUserRemoved(PRIMARY_USER_HANDLE) verify(mCallback, never()).accept(any(), any()) } @@ -483,4 +484,29 @@ class SatelliteAccessControllerTest { mRoleHolderChangedListener.onRoleHoldersChanged(RoleManager.ROLE_SMS, PRIMARY_USER_HANDLE) inOrder.verify(mCallback).accept(emptySet(), setOf(TEST_UID1, smsUid)) } + + @FeatureFlag(name = CONSTRAINED_DATA_SATELLITE_OPTIN) + @Test + fun test_onExternalApplicationsAvailable() { + // Mock the sms apps as general opt-in apps without setting sms-role. + mockIsSatelliteDataOptimizedApp(SMS_APP1, true) + mockIsSatelliteDataOptimizedApp(SMS_APP2, true) + + val inOrder = inOrder(mCallback) + mSatelliteAccessController + .onUserAddedWithInstalledPackageList(PRIMARY_USER_HANDLE, emptyList()) + mSatelliteAccessController + .onUserAddedWithInstalledPackageList( + SECONDARY_USER_HANDLE, + emptyList() + ) + mSatelliteAccessController.onExternalApplicationsAvailable(arrayOf(SMS_APP1, SMS_APP2)) + inOrder.verify(mCallback).accept(emptySet(), setOf( + SMS_APP_ID1.toUid(PRIMARY_USER), + SMS_APP_ID1.toUid(SECONDARY_USER), + SMS_APP_ID2.toUid(PRIMARY_USER), + SMS_APP_ID2.toUid(SECONDARY_USER) + )) + inOrder.verifyNoMoreInteractions() + } }