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.ranging; 18 19 import static android.Manifest.permission.RANGING; 20 import static android.bluetooth.BluetoothDevice.BOND_BONDED; 21 import static android.permission.PermissionManager.PERMISSION_GRANTED; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.SuppressLint; 26 import android.app.ActivityManager; 27 import android.bluetooth.BluetoothManager; 28 import android.content.AttributionSource; 29 import android.content.Context; 30 import android.content.pm.ApplicationInfo; 31 import android.content.pm.PackageManager; 32 import android.os.Binder; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.os.Looper; 36 import android.os.Process; 37 import android.os.UserHandle; 38 import android.permission.PermissionManager; 39 import android.util.Log; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.server.ranging.CapabilitiesProvider.CapabilitiesAdapter; 43 import com.android.server.ranging.blerssi.BleRssiAdapter; 44 import com.android.server.ranging.blerssi.BleRssiCapabilitiesAdapter; 45 import com.android.server.ranging.cs.CsAdapter; 46 import com.android.server.ranging.cs.CsCapabilitiesAdapter; 47 import com.android.server.ranging.oob.OobController; 48 import com.android.server.ranging.rtt.RttAdapter; 49 import com.android.server.ranging.rtt.RttCapabilitiesAdapter; 50 import com.android.server.ranging.session.RangingSessionConfig; 51 import com.android.server.ranging.uwb.UwbAdapter; 52 import com.android.server.ranging.uwb.UwbCapabilitiesAdapter; 53 54 import com.google.common.util.concurrent.ListeningExecutorService; 55 56 import java.util.Arrays; 57 import java.util.HashMap; 58 import java.util.Map; 59 import java.util.Objects; 60 61 public class RangingInjector { 62 63 private static final String TAG = "RangingInjector"; 64 65 private static final int APP_INFO_FLAGS_SYSTEM_APP = 66 ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 67 68 private final Context mContext; 69 private final RangingServiceManager mRangingServiceManager; 70 private final OobController mOobController; 71 72 private final CapabilitiesProvider mCapabilitiesProvider; 73 private final PermissionManager mPermissionManager; 74 75 private final Looper mLooper; 76 77 private final Handler mAlarmHandler; 78 private final DeviceConfigFacade mDeviceConfigFacade; 79 80 @SuppressLint("StaticFieldLeak") 81 private static RangingInjector sInstance; 82 RangingInjector(@onNull Context context)83 public RangingInjector(@NonNull Context context) { 84 HandlerThread rangingHandlerThread = new HandlerThread("RangingServiceHandler"); 85 rangingHandlerThread.start(); 86 mLooper = rangingHandlerThread.getLooper(); 87 mContext = context; 88 mCapabilitiesProvider = new CapabilitiesProvider(this); 89 mRangingServiceManager = new RangingServiceManager(this, 90 mContext.getSystemService(ActivityManager.class), 91 mLooper); 92 mOobController = new OobController(this); 93 mPermissionManager = context.getSystemService(PermissionManager.class); 94 mAlarmHandler = new Handler(mLooper); 95 mDeviceConfigFacade = new DeviceConfigFacade(new Handler(mLooper), mContext); 96 sInstance = this; 97 } 98 getInstance()99 public static RangingInjector getInstance() { 100 return Objects.requireNonNull(sInstance); 101 } 102 103 @VisibleForTesting setInstance(RangingInjector rangingInjector)104 public static void setInstance(RangingInjector rangingInjector) { 105 sInstance = rangingInjector; 106 } 107 getContext()108 public Context getContext() { 109 return mContext; 110 } 111 getCapabilitiesProvider()112 public CapabilitiesProvider getCapabilitiesProvider() { 113 return mCapabilitiesProvider; 114 } 115 getRangingServiceManager()116 public RangingServiceManager getRangingServiceManager() { 117 return mRangingServiceManager; 118 } 119 getOobController()120 public OobController getOobController() { 121 return mOobController; 122 } 123 getAlarmHandler()124 public Handler getAlarmHandler() { 125 return mAlarmHandler; 126 } 127 getDeviceConfigFacade()128 public DeviceConfigFacade getDeviceConfigFacade() { 129 return mDeviceConfigFacade; 130 } 131 132 /** 133 * Create a new adapter for a technology. 134 */ createAdapter( @onNull AttributionSource attributionSource, @NonNull RangingSessionConfig.TechnologyConfig config, @NonNull ListeningExecutorService executor )135 public @NonNull RangingAdapter createAdapter( 136 @NonNull AttributionSource attributionSource, 137 @NonNull RangingSessionConfig.TechnologyConfig config, 138 @NonNull ListeningExecutorService executor 139 ) { 140 switch (config.getTechnology()) { 141 case UWB: 142 return new UwbAdapter( 143 mContext, this, attributionSource, executor, config.getDeviceRole()); 144 case CS: 145 return new CsAdapter(mContext, this); 146 case RTT: 147 return new RttAdapter(mContext, this, executor, config.getDeviceRole()); 148 case RSSI: 149 return new BleRssiAdapter(mContext, this); 150 default: 151 throw new IllegalArgumentException( 152 "Adapter does not exist for technology " + config.getTechnology()); 153 } 154 } 155 createCapabilitiesAdapter( @onNull RangingTechnology technology, @NonNull CapabilitiesProvider.TechnologyAvailabilityListener listener )156 public @NonNull CapabilitiesAdapter createCapabilitiesAdapter( 157 @NonNull RangingTechnology technology, 158 @NonNull CapabilitiesProvider.TechnologyAvailabilityListener listener 159 ) { 160 switch (technology) { 161 case UWB: 162 return new UwbCapabilitiesAdapter(mContext, listener); 163 case CS: 164 return new CsCapabilitiesAdapter(mContext, listener); 165 case RTT: 166 return new RttCapabilitiesAdapter(mContext, listener); 167 case RSSI: 168 return new BleRssiCapabilitiesAdapter(mContext, listener); 169 default: 170 throw new IllegalArgumentException( 171 "CapabilitiesAdapter does not exist for technology " + technology); 172 } 173 } 174 enforceRangingPermissionForPreflight( @onNull AttributionSource attributionSource)175 public void enforceRangingPermissionForPreflight( 176 @NonNull AttributionSource attributionSource) { 177 if (!attributionSource.checkCallingUid()) { 178 throw new SecurityException("Invalid attribution source " + attributionSource 179 + ", callingUid: " + Binder.getCallingUid()); 180 } 181 int permissionCheckResult = mPermissionManager.checkPermissionForPreflight( 182 RANGING, attributionSource); 183 if (permissionCheckResult != PERMISSION_GRANTED) { 184 throw new SecurityException("Caller does not hold RANGING permission"); 185 } 186 } 187 checkUwbRangingPermissionForStartDataDelivery( @onNull AttributionSource attributionSource, @NonNull String message)188 public boolean checkUwbRangingPermissionForStartDataDelivery( 189 @NonNull AttributionSource attributionSource, @NonNull String message) { 190 int permissionCheckResult = mPermissionManager.checkPermissionForStartDataDelivery( 191 RANGING, attributionSource, message); 192 return permissionCheckResult == PERMISSION_GRANTED; 193 } 194 195 /** Helper method to check if the app is a system app. */ isSystemApp(int uid, @NonNull String packageName)196 public boolean isSystemApp(int uid, @NonNull String packageName) { 197 try { 198 ApplicationInfo info = createPackageContextAsUser(uid) 199 .getPackageManager() 200 .getApplicationInfo(packageName, 0); 201 return (info.flags & APP_INFO_FLAGS_SYSTEM_APP) != 0; 202 } catch (PackageManager.NameNotFoundException e) { 203 // In case of exception, assume unknown app (more strict checking) 204 // Note: This case will never happen since checkPackage is 205 // called to verify validity before checking App's version. 206 Log.e(TAG, "Failed to get the app info", e); 207 } 208 return false; 209 } 210 211 /** 212 * Helper method creating a context based on the app's uid (to deal with multi user scenarios) 213 */ 214 @Nullable createPackageContextAsUser(int uid)215 private Context createPackageContextAsUser(int uid) { 216 Context userContext; 217 try { 218 userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0, 219 UserHandle.getUserHandleForUid(uid)); 220 } catch (PackageManager.NameNotFoundException e) { 221 Log.e(TAG, "Unknown package name"); 222 return null; 223 } 224 if (userContext == null) { 225 Log.e(TAG, "Unable to retrieve user context for " + uid); 226 return null; 227 } 228 return userContext; 229 } 230 231 @Nullable getAnyNonPrivilegedAppInAttributionSource(AttributionSource source)232 public AttributionSource getAnyNonPrivilegedAppInAttributionSource(AttributionSource source) { 233 // Iterate attribution source chain to ensure that there is no non-fg 3p app in the 234 // request. 235 AttributionSource attributionSource = source; 236 while (attributionSource != null) { 237 int uid = attributionSource.getUid(); 238 String packageName = attributionSource.getPackageName(); 239 if (!isPrivilegedApp(uid, packageName)) { 240 return attributionSource; 241 } 242 attributionSource = attributionSource.getNext(); 243 } 244 return null; 245 } 246 247 /** Whether the uid is signed with the same key as the platform. */ isAppSignedWithPlatformKey(int uid)248 public boolean isAppSignedWithPlatformKey(int uid) { 249 return mContext.getPackageManager().checkSignatures(uid, Process.SYSTEM_UID) 250 == PackageManager.SIGNATURE_MATCH; 251 } 252 253 /** Helper method to check if the app is from foreground app/service. */ isForegroundAppOrServiceImportance(int importance)254 public static boolean isForegroundAppOrServiceImportance(int importance) { 255 return importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 256 } 257 258 /** Helper method to check if the app or service is no longer running. */ isNonExistentAppOrService(int importance)259 public static boolean isNonExistentAppOrService(int importance) { 260 return importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; 261 } 262 isPrivilegedApp(int uid, String packageName)263 public boolean isPrivilegedApp(int uid, String packageName) { 264 return isSystemApp(uid, packageName) || isAppSignedWithPlatformKey(uid); 265 } 266 267 /** Helper method to check if the app is from foreground app/service. */ isForegroundAppOrService(int uid, @NonNull String packageName)268 public boolean isForegroundAppOrService(int uid, @NonNull String packageName) { 269 long identity = Binder.clearCallingIdentity(); 270 try { 271 return isForegroundAppOrServiceImportance(getPackageImportance(uid, packageName)); 272 } catch (SecurityException e) { 273 Log.e(TAG, "Failed to retrieve the app importance", e); 274 return false; 275 } finally { 276 Binder.restoreCallingIdentity(identity); 277 } 278 } 279 280 /** Helper method to retrieve app importance. */ getPackageImportance(int uid, @NonNull String packageName)281 private int getPackageImportance(int uid, @NonNull String packageName) { 282 if (sOverridePackageImportance.containsKey(packageName)) { 283 Log.w(TAG, "Overriding package importance for testing"); 284 return sOverridePackageImportance.get(packageName); 285 } 286 try { 287 return createPackageContextAsUser(uid) 288 .getSystemService(ActivityManager.class) 289 .getPackageImportance(packageName); 290 } catch (SecurityException e) { 291 Log.e(TAG, "Failed to retrieve the app importance", e); 292 return ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; 293 } 294 } 295 296 private static Map<String, Integer> sOverridePackageImportance = new HashMap(); 297 298 // Use this if we have adb shell command support setOverridePackageImportance(String packageName, int importance)299 public void setOverridePackageImportance(String packageName, int importance) { 300 sOverridePackageImportance.put(packageName, importance); 301 } resetOverridePackageImportance(String packageName)302 public void resetOverridePackageImportance(String packageName) { 303 sOverridePackageImportance.remove(packageName); 304 } 305 306 /** 307 * Valid Bluetooth hardware addresses must be upper case, in big endian byte order, and in a 308 * format such as "00:11:22:33:AA:BB". 309 */ isRemoteDeviceBluetoothBonded(String btAddress)310 public boolean isRemoteDeviceBluetoothBonded(String btAddress) { 311 long identity = Binder.clearCallingIdentity(); 312 try { 313 return mContext.getSystemService(BluetoothManager.class) 314 .getAdapter() 315 .getRemoteDevice(btAddress) 316 .getBondState() == BOND_BONDED; 317 } finally { 318 Binder.restoreCallingIdentity(identity); 319 } 320 } 321 isRangingTechnologyEnabled(RangingTechnology rangingTechnology)322 public boolean isRangingTechnologyEnabled(RangingTechnology rangingTechnology) { 323 return Arrays.asList(getDeviceConfigFacade().getTechnologyPreferenceList()).contains( 324 rangingTechnology.toString() 325 ); 326 } 327 } 328