Skip to content
Snippets Groups Projects
Commit 33c7da36 authored by Nagendra Prasad Nagarle Basavaraju's avatar Nagendra Prasad Nagarle Basavaraju
Browse files

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
parent 1fa6f090
No related branches found
No related tags found
No related merge requests found
...@@ -179,6 +179,8 @@ java_library { ...@@ -179,6 +179,8 @@ java_library {
"unsupportedappusage", "unsupportedappusage",
"ServiceConnectivityResources", "ServiceConnectivityResources",
"framework-statsd", "framework-statsd",
"framework-permission",
"framework-permission-s",
], ],
static_libs: [ static_libs: [
// Do not add libs here if they are already included // Do not add libs here if they are already included
...@@ -265,6 +267,8 @@ java_defaults { ...@@ -265,6 +267,8 @@ java_defaults {
"framework-tethering.impl", "framework-tethering.impl",
"framework-wifi", "framework-wifi",
"libprotobuf-java-nano", "libprotobuf-java-nano",
"framework-permission",
"framework-permission-s",
], ],
jarjar_rules: ":connectivity-jarjar-rules", jarjar_rules: ":connectivity-jarjar-rules",
apex_available: [ apex_available: [
......
/*
* 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();
}
}
/*
* 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())
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment