1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.connectivity; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.app.role.OnRoleHoldersChangedListener; 22 import android.app.role.RoleManager; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageManager; 29 import android.os.Handler; 30 import android.os.Process; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.util.ArraySet; 34 import android.util.Log; 35 import android.util.SparseArray; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 39 import java.util.List; 40 import java.util.Set; 41 import java.util.concurrent.Executor; 42 import java.util.function.Consumer; 43 44 /** 45 * Tracks the uid of all the default messaging application which are role_sms role and 46 * satellite_communication permission complaint and requests ConnectivityService to create multi 47 * layer request with satellite internet access support for the default message application. 48 * @hide 49 */ 50 public class SatelliteAccessController { 51 private static final String TAG = SatelliteAccessController.class.getSimpleName(); 52 private final Context mContext; 53 private final Dependencies mDeps; 54 private final DefaultMessageRoleListener mDefaultMessageRoleListener; 55 private final Consumer<Set<Integer>> mCallback; 56 private final Handler mConnectivityServiceHandler; 57 58 // At this sparseArray, Key is userId and values are uids of SMS apps that are allowed 59 // to use satellite network as fallback. 60 private final SparseArray<Set<Integer>> mAllUsersSatelliteNetworkFallbackUidCache = 61 new SparseArray<>(); 62 63 /** 64 * Monitor {@link android.app.role.OnRoleHoldersChangedListener#onRoleHoldersChanged(String, 65 * UserHandle)}, 66 * 67 */ 68 private final class DefaultMessageRoleListener 69 implements OnRoleHoldersChangedListener { 70 @Override onRoleHoldersChanged(String role, UserHandle userHandle)71 public void onRoleHoldersChanged(String role, UserHandle userHandle) { 72 if (RoleManager.ROLE_SMS.equals(role)) { 73 Log.i(TAG, "ROLE_SMS Change detected "); 74 onRoleSmsChanged(userHandle); 75 } 76 } 77 register()78 public void register() { 79 try { 80 mDeps.addOnRoleHoldersChangedListenerAsUser( 81 mConnectivityServiceHandler::post, this, UserHandle.ALL); 82 } catch (RuntimeException e) { 83 Log.wtf(TAG, "Could not register satellite controller listener due to " + e); 84 } 85 } 86 } 87 SatelliteAccessController(@onNull final Context c, Consumer<Set<Integer>> callback, @NonNull final Handler connectivityServiceInternalHandler)88 public SatelliteAccessController(@NonNull final Context c, 89 Consumer<Set<Integer>> callback, 90 @NonNull final Handler connectivityServiceInternalHandler) { 91 this(c, new Dependencies(c), callback, connectivityServiceInternalHandler); 92 } 93 94 public static class Dependencies { 95 private final RoleManager mRoleManager; 96 Dependencies(Context context)97 private Dependencies(Context context) { 98 mRoleManager = context.getSystemService(RoleManager.class); 99 } 100 101 /** See {@link RoleManager#getRoleHoldersAsUser(String, UserHandle)} */ getRoleHoldersAsUser(String roleName, UserHandle userHandle)102 public List<String> getRoleHoldersAsUser(String roleName, UserHandle userHandle) { 103 return mRoleManager.getRoleHoldersAsUser(roleName, userHandle); 104 } 105 106 /** See {@link RoleManager#addOnRoleHoldersChangedListenerAsUser} */ addOnRoleHoldersChangedListenerAsUser(@onNull Executor executor, @NonNull OnRoleHoldersChangedListener listener, UserHandle user)107 public void addOnRoleHoldersChangedListenerAsUser(@NonNull Executor executor, 108 @NonNull OnRoleHoldersChangedListener listener, UserHandle user) { 109 mRoleManager.addOnRoleHoldersChangedListenerAsUser(executor, listener, user); 110 } 111 } 112 113 @VisibleForTesting SatelliteAccessController(@onNull final Context c, @NonNull final Dependencies deps, Consumer<Set<Integer>> callback, @NonNull final Handler connectivityServiceInternalHandler)114 SatelliteAccessController(@NonNull final Context c, @NonNull final Dependencies deps, 115 Consumer<Set<Integer>> callback, 116 @NonNull final Handler connectivityServiceInternalHandler) { 117 mContext = c; 118 mDeps = deps; 119 mDefaultMessageRoleListener = new DefaultMessageRoleListener(); 120 mCallback = callback; 121 mConnectivityServiceHandler = connectivityServiceInternalHandler; 122 } 123 updateSatelliteNetworkFallbackUidListCache(List<String> packageNames, @NonNull UserHandle userHandle)124 private Set<Integer> updateSatelliteNetworkFallbackUidListCache(List<String> packageNames, 125 @NonNull UserHandle userHandle) { 126 Set<Integer> fallbackUids = new ArraySet<>(); 127 PackageManager pm = 128 mContext.createContextAsUser(userHandle, 0).getPackageManager(); 129 if (pm != null) { 130 for (String packageName : packageNames) { 131 // Check if SATELLITE_COMMUNICATION permission is enabled for default sms 132 // application package before adding it part of satellite network fallback uid 133 // cache list. 134 if (isSatellitePermissionEnabled(pm, packageName)) { 135 int uid = getUidForPackage(pm, packageName); 136 if (uid != Process.INVALID_UID) { 137 fallbackUids.add(uid); 138 } 139 } 140 } 141 } else { 142 Log.wtf(TAG, "package manager found null"); 143 } 144 return fallbackUids; 145 } 146 147 //Check if satellite communication is enabled for the package isSatellitePermissionEnabled(PackageManager packageManager, String packageName)148 private boolean isSatellitePermissionEnabled(PackageManager packageManager, 149 String packageName) { 150 return packageManager.checkPermission( 151 Manifest.permission.SATELLITE_COMMUNICATION, packageName) 152 == PackageManager.PERMISSION_GRANTED; 153 } 154 getUidForPackage(PackageManager packageManager, String pkgName)155 private int getUidForPackage(PackageManager packageManager, String pkgName) { 156 if (pkgName == null) { 157 return Process.INVALID_UID; 158 } 159 try { 160 ApplicationInfo applicationInfo = packageManager.getApplicationInfo(pkgName, 0); 161 return applicationInfo.uid; 162 } catch (PackageManager.NameNotFoundException exception) { 163 Log.e(TAG, "Unable to find uid for package: " + pkgName); 164 } 165 return Process.INVALID_UID; 166 } 167 168 // on Role sms change triggered by OnRoleHoldersChangedListener() onRoleSmsChanged(@onNull UserHandle userHandle)169 private void onRoleSmsChanged(@NonNull UserHandle userHandle) { 170 int userId = userHandle.getIdentifier(); 171 if (userId == Process.INVALID_UID) { 172 Log.wtf(TAG, "Invalid User Id"); 173 return; 174 } 175 176 //Returns empty list if no package exists 177 final List<String> packageNames = 178 mDeps.getRoleHoldersAsUser(RoleManager.ROLE_SMS, userHandle); 179 180 // Store previous satellite fallback uid available 181 final Set<Integer> prevUidsForUser = 182 mAllUsersSatelliteNetworkFallbackUidCache.get(userId, new ArraySet<>()); 183 184 Log.i(TAG, "currentUser : role_sms_packages: " + userId + " : " + packageNames); 185 final Set<Integer> newUidsForUser = 186 updateSatelliteNetworkFallbackUidListCache(packageNames, userHandle); 187 Log.i(TAG, "satellite_fallback_uid: " + newUidsForUser); 188 189 // on Role change, update the multilayer request at ConnectivityService with updated 190 // satellite network fallback uid cache list of multiple users as applicable 191 if (newUidsForUser.equals(prevUidsForUser)) { 192 return; 193 } 194 195 mAllUsersSatelliteNetworkFallbackUidCache.put(userId, newUidsForUser); 196 197 // Update all users fallback cache for user, send cs fallback to update ML request 198 reportSatelliteNetworkFallbackUids(); 199 } 200 reportSatelliteNetworkFallbackUids()201 private void reportSatelliteNetworkFallbackUids() { 202 // Merge all uids of multiple users available 203 Set<Integer> mergedSatelliteNetworkFallbackUidCache = new ArraySet<>(); 204 for (int i = 0; i < mAllUsersSatelliteNetworkFallbackUidCache.size(); i++) { 205 mergedSatelliteNetworkFallbackUidCache.addAll( 206 mAllUsersSatelliteNetworkFallbackUidCache.valueAt(i)); 207 } 208 Log.i(TAG, "merged uid list for multi layer request : " 209 + mergedSatelliteNetworkFallbackUidCache); 210 211 // trigger multiple layer request for satellite network fallback of multi user uids 212 mCallback.accept(mergedSatelliteNetworkFallbackUidCache); 213 } 214 start()215 public void start() { 216 mConnectivityServiceHandler.post(this::updateAllUserRoleSmsUids); 217 218 // register sms OnRoleHoldersChangedListener 219 mDefaultMessageRoleListener.register(); 220 221 // Monitor for User removal intent, to update satellite fallback uids. 222 IntentFilter userRemovedFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 223 mContext.registerReceiver(new BroadcastReceiver() { 224 @Override 225 public void onReceive(Context context, Intent intent) { 226 final String action = intent.getAction(); 227 if (Intent.ACTION_USER_REMOVED.equals(action)) { 228 final UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); 229 if (userHandle == null) return; 230 updateSatelliteFallbackUidListOnUserRemoval(userHandle.getIdentifier()); 231 } else { 232 Log.wtf(TAG, "received unexpected intent: " + action); 233 } 234 } 235 }, userRemovedFilter, null, mConnectivityServiceHandler); 236 237 } 238 updateAllUserRoleSmsUids()239 private void updateAllUserRoleSmsUids() { 240 UserManager userManager = mContext.getSystemService(UserManager.class); 241 // get existing user handles of available users 242 List<UserHandle> existingUsers = userManager.getUserHandles(true /*excludeDying*/); 243 244 // Iterate through the user handles and obtain their uids with role sms and satellite 245 // communication permission 246 Log.i(TAG, "existing users: " + existingUsers); 247 for (UserHandle userHandle : existingUsers) { 248 onRoleSmsChanged(userHandle); 249 } 250 } 251 updateSatelliteFallbackUidListOnUserRemoval(int userIdRemoved)252 private void updateSatelliteFallbackUidListOnUserRemoval(int userIdRemoved) { 253 Log.i(TAG, "user id removed:" + userIdRemoved); 254 if (mAllUsersSatelliteNetworkFallbackUidCache.contains(userIdRemoved)) { 255 mAllUsersSatelliteNetworkFallbackUidCache.remove(userIdRemoved); 256 reportSatelliteNetworkFallbackUids(); 257 } 258 } 259 } 260