Listen to onExternalApplicationsAvailable in SatelliteAccessController

For maintaining constrained data satellite opt-in apps, this is
needed for tracking apps installed on the storage.

Flag: EXEMPT flagged with a kill switch
Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.server.connectivity.SatelliteAccessControllerTest \
  --update-device
Bug: 410731080
Change-Id: I2a686a5a37747de97dcee6b78f66965cb4fd9942
This commit is contained in:
Junyu Lai 2025-04-25 17:40:52 +08:00
commit 63dbd3ec93
3 changed files with 78 additions and 2 deletions

View file

@ -7796,6 +7796,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) {

View file

@ -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<Integer> mSatelliteDataOptimizedUids = new ArraySet<>();
private final ArrayMap<UserHandle, PackageManager> 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<Integer> 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

View file

@ -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<PackageInfo>())
mSatelliteAccessController
.onUserAddedWithInstalledPackageList(
SECONDARY_USER_HANDLE,
emptyList<PackageInfo>()
)
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()
}
}