Add SatelliteAccessController class to support messaging on Satellite Internet
- create SatelliteAccessController class to identify role sms and satellite communication enabled uid's, to keep track of role changes through OnRoleHoldersChangedListener and to request to create multi layer request to ConnectivityService. - unit test coverage Bug: 320514105 Test: m and atest FrameworksNetTests Change-Id: I95086f7a4fd6fe1261f432e605d792204ff5310b
This commit is contained in:
parent
1fa6f09019
commit
33c7da3661
3 changed files with 374 additions and 0 deletions
|
|
@ -179,6 +179,8 @@ java_library {
|
|||
"unsupportedappusage",
|
||||
"ServiceConnectivityResources",
|
||||
"framework-statsd",
|
||||
"framework-permission",
|
||||
"framework-permission-s",
|
||||
],
|
||||
static_libs: [
|
||||
// Do not add libs here if they are already included
|
||||
|
|
@ -265,6 +267,8 @@ java_defaults {
|
|||
"framework-tethering.impl",
|
||||
"framework-wifi",
|
||||
"libprotobuf-java-nano",
|
||||
"framework-permission",
|
||||
"framework-permission-s",
|
||||
],
|
||||
jarjar_rules: ":connectivity-jarjar-rules",
|
||||
apex_available: [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.role.OnRoleHoldersChangedListener;
|
||||
import android.app.role.RoleManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Tracks the uid of all the default messaging application which are role_sms role and
|
||||
* satellite_communication permission complaint and requests ConnectivityService to create multi
|
||||
* layer request with satellite internet access support for the default message application.
|
||||
* @hide
|
||||
*/
|
||||
public class SatelliteAccessController {
|
||||
private static final String TAG = SatelliteAccessController.class.getSimpleName();
|
||||
private final PackageManager mPackageManager;
|
||||
private final Dependencies mDeps;
|
||||
private final DefaultMessageRoleListener mDefaultMessageRoleListener;
|
||||
private final Consumer<Set<Integer>> mCallback;
|
||||
private final Set<Integer> mSatelliteNetworkPreferredUidCache = new ArraySet<>();
|
||||
private final Handler mConnectivityServiceHandler;
|
||||
|
||||
/**
|
||||
* Monitor {@link android.app.role.OnRoleHoldersChangedListener#onRoleHoldersChanged(String,
|
||||
* UserHandle)},
|
||||
*
|
||||
*/
|
||||
private final class DefaultMessageRoleListener
|
||||
implements OnRoleHoldersChangedListener {
|
||||
@Override
|
||||
public void onRoleHoldersChanged(String role, UserHandle user) {
|
||||
if (RoleManager.ROLE_SMS.equals(role)) {
|
||||
Log.i(TAG, "ROLE_SMS Change detected ");
|
||||
onRoleSmsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void register() {
|
||||
try {
|
||||
mDeps.addOnRoleHoldersChangedListenerAsUser(
|
||||
mConnectivityServiceHandler::post, this, UserHandle.ALL);
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "Could not register satellite controller listener due to " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SatelliteAccessController(@NonNull final Context c,
|
||||
Consumer<Set<Integer>> callback,
|
||||
@NonNull final Handler connectivityServiceInternalHandler) {
|
||||
this(c, new Dependencies(c), callback, connectivityServiceInternalHandler);
|
||||
}
|
||||
|
||||
public static class Dependencies {
|
||||
private final RoleManager mRoleManager;
|
||||
|
||||
private Dependencies(Context context) {
|
||||
mRoleManager = context.getSystemService(RoleManager.class);
|
||||
}
|
||||
|
||||
/** See {@link RoleManager#getRoleHolders(String)} */
|
||||
public List<String> getRoleHolders(String roleName) {
|
||||
return mRoleManager.getRoleHolders(roleName);
|
||||
}
|
||||
|
||||
/** See {@link RoleManager#addOnRoleHoldersChangedListenerAsUser} */
|
||||
public void addOnRoleHoldersChangedListenerAsUser(@NonNull Executor executor,
|
||||
@NonNull OnRoleHoldersChangedListener listener, UserHandle user) {
|
||||
mRoleManager.addOnRoleHoldersChangedListenerAsUser(executor, listener, user);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SatelliteAccessController(@NonNull final Context c, @NonNull final Dependencies deps,
|
||||
Consumer<Set<Integer>> callback,
|
||||
@NonNull final Handler connectivityServiceInternalHandler) {
|
||||
mDeps = deps;
|
||||
mPackageManager = c.getPackageManager();
|
||||
mDefaultMessageRoleListener = new DefaultMessageRoleListener();
|
||||
mCallback = callback;
|
||||
mConnectivityServiceHandler = connectivityServiceInternalHandler;
|
||||
}
|
||||
|
||||
private void updateSatelliteNetworkPreferredUidListCache(List<String> packageNames) {
|
||||
for (String packageName : packageNames) {
|
||||
// Check if SATELLITE_COMMUNICATION permission is enabled for default sms application
|
||||
// package before adding it part of satellite network preferred uid cache list.
|
||||
if (isSatellitePermissionEnabled(packageName)) {
|
||||
mSatelliteNetworkPreferredUidCache.add(getUidForPackage(packageName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check if satellite communication is enabled for the package
|
||||
private boolean isSatellitePermissionEnabled(String packageName) {
|
||||
if (mPackageManager != null) {
|
||||
return mPackageManager.checkPermission(
|
||||
Manifest.permission.SATELLITE_COMMUNICATION, packageName)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getUidForPackage(String pkgName) {
|
||||
if (pkgName == null) {
|
||||
return Process.INVALID_UID;
|
||||
}
|
||||
try {
|
||||
if (mPackageManager != null) {
|
||||
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkgName, 0);
|
||||
if (applicationInfo != null) {
|
||||
return applicationInfo.uid;
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException exception) {
|
||||
Log.e(TAG, "Unable to find uid for package: " + pkgName);
|
||||
}
|
||||
return Process.INVALID_UID;
|
||||
}
|
||||
|
||||
//on Role sms change triggered by OnRoleHoldersChangedListener()
|
||||
private void onRoleSmsChanged() {
|
||||
final List<String> packageNames = getRoleSmsChangedPackageName();
|
||||
|
||||
// Create a new Set
|
||||
Set<Integer> previousSatellitePreferredUid = new ArraySet<>(
|
||||
mSatelliteNetworkPreferredUidCache);
|
||||
|
||||
mSatelliteNetworkPreferredUidCache.clear();
|
||||
|
||||
if (packageNames != null) {
|
||||
Log.i(TAG, "role_sms_packages: " + packageNames);
|
||||
// On Role change listener, update the satellite network preferred uid cache list
|
||||
updateSatelliteNetworkPreferredUidListCache(packageNames);
|
||||
Log.i(TAG, "satellite_preferred_uid: " + mSatelliteNetworkPreferredUidCache);
|
||||
} else {
|
||||
Log.wtf(TAG, "package name was found null");
|
||||
}
|
||||
|
||||
// on Role change, update the multilayer request at ConnectivityService with updated
|
||||
// satellite network preferred uid cache list if changed or to revoke for previous default
|
||||
// sms app
|
||||
if (!mSatelliteNetworkPreferredUidCache.equals(previousSatellitePreferredUid)) {
|
||||
Log.i(TAG, "update multi layer request");
|
||||
mCallback.accept(mSatelliteNetworkPreferredUidCache);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getRoleSmsChangedPackageName() {
|
||||
try {
|
||||
return mDeps.getRoleHolders(RoleManager.ROLE_SMS);
|
||||
} catch (RuntimeException e) {
|
||||
Log.wtf(TAG, "Could not get package name at role sms change update due to: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Register OnRoleHoldersChangedListener */
|
||||
public void start() {
|
||||
mConnectivityServiceHandler.post(this::onRoleSmsChanged);
|
||||
mDefaultMessageRoleListener.register();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.connectivity
|
||||
|
||||
import android.Manifest
|
||||
import android.app.role.OnRoleHoldersChangedListener
|
||||
import android.app.role.RoleManager
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.UserHandle
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.function.Consumer
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val DEFAULT_MESSAGING_APP1 = "default_messaging_app_1"
|
||||
private const val DEFAULT_MESSAGING_APP2 = "default_messaging_app_2"
|
||||
private const val DEFAULT_MESSAGING_APP1_UID = 1234
|
||||
private const val DEFAULT_MESSAGING_APP2_UID = 5678
|
||||
|
||||
@RunWith(DevSdkIgnoreRunner::class)
|
||||
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
|
||||
class SatelliteAccessControllerTest {
|
||||
private val context = mock(Context::class.java)
|
||||
private val mPackageManager = mock(PackageManager::class.java)
|
||||
private val mHandler = mock(Handler::class.java)
|
||||
private val mRoleManager =
|
||||
mock(SatelliteAccessController.Dependencies::class.java)
|
||||
private val mCallback = mock(Consumer::class.java) as Consumer<Set<Int>>
|
||||
private val mSatelliteAccessController by lazy {
|
||||
SatelliteAccessController(context, mRoleManager, mCallback, mHandler)}
|
||||
private var mRoleHolderChangedListener: OnRoleHoldersChangedListener? = null
|
||||
@Before
|
||||
@Throws(PackageManager.NameNotFoundException::class)
|
||||
fun setup() {
|
||||
doReturn(mPackageManager).`when`(context).packageManager
|
||||
doReturn(PackageManager.PERMISSION_GRANTED)
|
||||
.`when`(mPackageManager)
|
||||
.checkPermission(Manifest.permission.SATELLITE_COMMUNICATION, DEFAULT_MESSAGING_APP1)
|
||||
doReturn(PackageManager.PERMISSION_GRANTED)
|
||||
.`when`(mPackageManager)
|
||||
.checkPermission(Manifest.permission.SATELLITE_COMMUNICATION, DEFAULT_MESSAGING_APP2)
|
||||
|
||||
// Initialise default message application package1
|
||||
val applicationInfo1 = ApplicationInfo()
|
||||
applicationInfo1.uid = DEFAULT_MESSAGING_APP1_UID
|
||||
doReturn(applicationInfo1)
|
||||
.`when`(mPackageManager)
|
||||
.getApplicationInfo(eq(DEFAULT_MESSAGING_APP1), anyInt())
|
||||
|
||||
// Initialise default message application package2
|
||||
val applicationInfo2 = ApplicationInfo()
|
||||
applicationInfo2.uid = DEFAULT_MESSAGING_APP2_UID
|
||||
doReturn(applicationInfo2)
|
||||
.`when`(mPackageManager)
|
||||
.getApplicationInfo(eq(DEFAULT_MESSAGING_APP2), anyInt())
|
||||
|
||||
// Get registered listener using captor
|
||||
val listenerCaptor = ArgumentCaptor.forClass(
|
||||
OnRoleHoldersChangedListener::class.java
|
||||
)
|
||||
mSatelliteAccessController.start()
|
||||
verify(mRoleManager).addOnRoleHoldersChangedListenerAsUser(
|
||||
any(Executor::class.java), listenerCaptor.capture(), any(UserHandle::class.java))
|
||||
mRoleHolderChangedListener = listenerCaptor.value
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_onRoleHoldersChanged_SatellitePreferredUid_Changed() {
|
||||
doReturn(listOf<String>()).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
|
||||
val satelliteNetworkPreferredSet =
|
||||
ArgumentCaptor.forClass(Set::class.java) as ArgumentCaptor<Set<Int>>
|
||||
mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
|
||||
verify(mCallback, never()).accept(satelliteNetworkPreferredSet.capture())
|
||||
|
||||
// check DEFAULT_MESSAGING_APP1 is available as satellite network preferred uid
|
||||
doReturn(listOf(DEFAULT_MESSAGING_APP1))
|
||||
.`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
|
||||
mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
|
||||
verify(mCallback).accept(satelliteNetworkPreferredSet.capture())
|
||||
var satelliteNetworkPreferredUids = satelliteNetworkPreferredSet.value
|
||||
assertEquals(1, satelliteNetworkPreferredUids.size)
|
||||
assertTrue(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP1_UID))
|
||||
assertFalse(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP2_UID))
|
||||
|
||||
// check DEFAULT_MESSAGING_APP1 and DEFAULT_MESSAGING_APP2 is available
|
||||
// as satellite network preferred uid
|
||||
val dmas: MutableList<String> = ArrayList()
|
||||
dmas.add(DEFAULT_MESSAGING_APP1)
|
||||
dmas.add(DEFAULT_MESSAGING_APP2)
|
||||
doReturn(dmas).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
|
||||
mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
|
||||
verify(mCallback, times(2))
|
||||
.accept(satelliteNetworkPreferredSet.capture())
|
||||
satelliteNetworkPreferredUids = satelliteNetworkPreferredSet.value
|
||||
assertEquals(2, satelliteNetworkPreferredUids.size)
|
||||
assertTrue(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP1_UID))
|
||||
assertTrue(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP2_UID))
|
||||
|
||||
// check no uid is available as satellite network preferred uid
|
||||
doReturn(listOf<String>()).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
|
||||
mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
|
||||
verify(mCallback, times(3))
|
||||
.accept(satelliteNetworkPreferredSet.capture())
|
||||
satelliteNetworkPreferredUids = satelliteNetworkPreferredSet.value
|
||||
assertEquals(0, satelliteNetworkPreferredUids.size)
|
||||
assertFalse(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP1_UID))
|
||||
assertFalse(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP2_UID))
|
||||
|
||||
// No Change received at OnRoleSmsChanged, check callback not triggered
|
||||
doReturn(listOf<String>()).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
|
||||
mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
|
||||
verify(mCallback, times(3))
|
||||
.accept(satelliteNetworkPreferredSet.capture())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_onRoleHoldersChanged_NoSatelliteCommunicationPermission() {
|
||||
doReturn(listOf<Any>()).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
|
||||
val satelliteNetworkPreferredSet =
|
||||
ArgumentCaptor.forClass(Set::class.java) as ArgumentCaptor<Set<Int>>
|
||||
mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
|
||||
verify(mCallback, never()).accept(satelliteNetworkPreferredSet.capture())
|
||||
|
||||
// check DEFAULT_MESSAGING_APP1 is not available as satellite network preferred uid
|
||||
// since satellite communication permission not available.
|
||||
doReturn(PackageManager.PERMISSION_DENIED)
|
||||
.`when`(mPackageManager)
|
||||
.checkPermission(Manifest.permission.SATELLITE_COMMUNICATION, DEFAULT_MESSAGING_APP1)
|
||||
doReturn(listOf(DEFAULT_MESSAGING_APP1))
|
||||
.`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
|
||||
mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
|
||||
verify(mCallback, never()).accept(satelliteNetworkPreferredSet.capture())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_onRoleHoldersChanged_RoleSms_NotAvailable() {
|
||||
doReturn(listOf(DEFAULT_MESSAGING_APP1))
|
||||
.`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
|
||||
val satelliteNetworkPreferredSet =
|
||||
ArgumentCaptor.forClass(Set::class.java) as ArgumentCaptor<Set<Int>>
|
||||
mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_BROWSER, UserHandle.ALL)
|
||||
verify(mCallback, never()).accept(satelliteNetworkPreferredSet.capture())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue