1 /* 2 * Copyright (C) 2021 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.uwb; 18 19 import static android.Manifest.permission.UWB_RANGING; 20 import static android.permission.PermissionManager.PERMISSION_GRANTED; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.app.ActivityManager; 25 import android.app.AlarmManager; 26 import android.content.ApexEnvironment; 27 import android.content.AttributionSource; 28 import android.content.Context; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageManager; 31 import android.os.Binder; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.Looper; 35 import android.os.SystemClock; 36 import android.os.SystemProperties; 37 import android.os.UserHandle; 38 import android.permission.PermissionManager; 39 import android.provider.Settings; 40 import android.util.AtomicFile; 41 import android.util.Log; 42 43 import com.android.server.uwb.jni.NativeUwbManager; 44 import com.android.server.uwb.multchip.UwbMultichipData; 45 46 import java.io.File; 47 import java.util.Locale; 48 49 /** 50 * To be used for dependency injection (especially helps mocking static dependencies). 51 */ 52 public class UwbInjector { 53 private static final String TAG = "UwbInjector"; 54 private static final String APEX_NAME = "com.android.uwb"; 55 private static final String VENDOR_SERVICE_NAME = "uwb_vendor"; 56 private static final String BOOT_DEFAULT_UWB_COUNTRY_CODE = "ro.boot.uwbcountrycode"; 57 58 /** 59 * The path where the Uwb apex is mounted. 60 * Current value = "/apex/com.android.uwb" 61 */ 62 private static final String UWB_APEX_PATH = 63 new File("/apex", APEX_NAME).getAbsolutePath(); 64 private static final int APP_INFO_FLAGS_SYSTEM_APP = 65 ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 66 67 private final UwbContext mContext; 68 private final Looper mLooper; 69 private final PermissionManager mPermissionManager; 70 private final UwbSettingsStore mUwbSettingsStore; 71 private final NativeUwbManager mNativeUwbManager; 72 private final UwbCountryCode mUwbCountryCode; 73 private final UwbServiceCore mUwbService; 74 private final UwbMetrics mUwbMetrics; 75 private final DeviceConfigFacade mDeviceConfigFacade; 76 private final UwbMultichipData mUwbMultichipData; 77 private final SystemBuildProperties mSystemBuildProperties; 78 private final UwbDiagnostics mUwbDiagnostics; 79 UwbInjector(@onNull UwbContext context)80 public UwbInjector(@NonNull UwbContext context) { 81 // Create UWB service thread. 82 HandlerThread uwbHandlerThread = new HandlerThread("UwbService"); 83 uwbHandlerThread.start(); 84 mLooper = uwbHandlerThread.getLooper(); 85 86 mContext = context; 87 mPermissionManager = context.getSystemService(PermissionManager.class); 88 mUwbSettingsStore = new UwbSettingsStore( 89 context, new Handler(mLooper), 90 new AtomicFile(new File(getDeviceProtectedDataDir(), 91 UwbSettingsStore.FILE_NAME)), this); 92 mNativeUwbManager = new NativeUwbManager(this); 93 mUwbCountryCode = 94 new UwbCountryCode(mContext, mNativeUwbManager, new Handler(mLooper), this); 95 mUwbMetrics = new UwbMetrics(this); 96 mDeviceConfigFacade = new DeviceConfigFacade(new Handler(mLooper), this); 97 mUwbMultichipData = new UwbMultichipData(mContext); 98 UwbConfigurationManager uwbConfigurationManager = 99 new UwbConfigurationManager(mNativeUwbManager); 100 UwbSessionNotificationManager uwbSessionNotificationManager = 101 new UwbSessionNotificationManager(this); 102 UwbSessionManager uwbSessionManager = 103 new UwbSessionManager(uwbConfigurationManager, mNativeUwbManager, mUwbMetrics, 104 uwbSessionNotificationManager, this, 105 mContext.getSystemService(AlarmManager.class), mLooper); 106 mUwbService = new UwbServiceCore(mContext, mNativeUwbManager, mUwbMetrics, 107 mUwbCountryCode, uwbSessionManager, uwbConfigurationManager, this, mLooper); 108 mSystemBuildProperties = new SystemBuildProperties(); 109 mUwbDiagnostics = new UwbDiagnostics(mContext, this, mSystemBuildProperties); 110 } 111 getUwbSettingsStore()112 public UwbSettingsStore getUwbSettingsStore() { 113 return mUwbSettingsStore; 114 } 115 getNativeUwbManager()116 public NativeUwbManager getNativeUwbManager() { 117 return mNativeUwbManager; 118 } 119 getUwbCountryCode()120 public UwbCountryCode getUwbCountryCode() { 121 return mUwbCountryCode; 122 } 123 getUwbMetrics()124 public UwbMetrics getUwbMetrics() { 125 return mUwbMetrics; 126 } 127 getDeviceConfigFacade()128 public DeviceConfigFacade getDeviceConfigFacade() { 129 return mDeviceConfigFacade; 130 } 131 getMultichipData()132 public UwbMultichipData getMultichipData() { 133 return mUwbMultichipData; 134 } 135 getUwbServiceCore()136 public UwbServiceCore getUwbServiceCore() { 137 return mUwbService; 138 } 139 getUwbDiagnostics()140 public UwbDiagnostics getUwbDiagnostics() { 141 return mUwbDiagnostics; 142 } 143 144 /** 145 * Create a UwbShellCommand instance. 146 */ makeUwbShellCommand(UwbServiceImpl uwbService)147 public UwbShellCommand makeUwbShellCommand(UwbServiceImpl uwbService) { 148 return new UwbShellCommand(this, uwbService, mContext); 149 } 150 151 /** 152 * Throws security exception if the UWB_RANGING permission is not granted for the calling app. 153 * 154 * <p>Should be used in situations where the app op should not be noted. 155 */ enforceUwbRangingPermissionForPreflight( @onNull AttributionSource attributionSource)156 public void enforceUwbRangingPermissionForPreflight( 157 @NonNull AttributionSource attributionSource) { 158 if (!attributionSource.checkCallingUid()) { 159 throw new SecurityException("Invalid attribution source " + attributionSource 160 + ", callingUid: " + Binder.getCallingUid()); 161 } 162 int permissionCheckResult = mPermissionManager.checkPermissionForPreflight( 163 UWB_RANGING, attributionSource); 164 if (permissionCheckResult != PERMISSION_GRANTED) { 165 throw new SecurityException("Caller does not hold UWB_RANGING permission"); 166 } 167 } 168 169 /** 170 * Returns true if the UWB_RANGING permission is granted for the calling app. 171 * 172 * <p>Should be used in situations where data will be delivered and hence the app op should 173 * be noted. 174 */ checkUwbRangingPermissionForDataDelivery( @onNull AttributionSource attributionSource, @NonNull String message)175 public boolean checkUwbRangingPermissionForDataDelivery( 176 @NonNull AttributionSource attributionSource, @NonNull String message) { 177 int permissionCheckResult = mPermissionManager.checkPermissionForDataDelivery( 178 UWB_RANGING, attributionSource, message); 179 return permissionCheckResult == PERMISSION_GRANTED; 180 } 181 182 /** 183 * Get device protected storage dir for the UWB apex. 184 */ 185 @NonNull getDeviceProtectedDataDir()186 public File getDeviceProtectedDataDir() { 187 return ApexEnvironment.getApexEnvironment(APEX_NAME).getDeviceProtectedDataDir(); 188 } 189 190 /** 191 * Get integer value from Settings. 192 * 193 * @throws Settings.SettingNotFoundException 194 */ getSettingsInt(@onNull String key)195 public int getSettingsInt(@NonNull String key) throws Settings.SettingNotFoundException { 196 return Settings.Global.getInt(mContext.getContentResolver(), key); 197 } 198 199 /** 200 * Get integer value from Settings. 201 */ getSettingsInt(@onNull String key, int defValue)202 public int getSettingsInt(@NonNull String key, int defValue) { 203 return Settings.Global.getInt(mContext.getContentResolver(), key, defValue); 204 } 205 206 /** 207 * Returns true if the app is in the Uwb apex, false otherwise. 208 * Checks if the app's path starts with "/apex/com.android.uwb". 209 */ isAppInUwbApex(ApplicationInfo appInfo)210 public static boolean isAppInUwbApex(ApplicationInfo appInfo) { 211 return appInfo.sourceDir.startsWith(UWB_APEX_PATH); 212 } 213 214 /** 215 * Get the current time of the clock in milliseconds. 216 * 217 * @return Current time in milliseconds. 218 */ getWallClockMillis()219 public long getWallClockMillis() { 220 return System.currentTimeMillis(); 221 } 222 223 /** 224 * Returns milliseconds since boot, including time spent in sleep. 225 * 226 * @return Current time since boot in milliseconds. 227 */ getElapsedSinceBootMillis()228 public long getElapsedSinceBootMillis() { 229 return SystemClock.elapsedRealtime(); 230 } 231 232 /** 233 * Returns nanoseconds since boot, including time spent in sleep. 234 * 235 * @return Current time since boot in milliseconds. 236 */ getElapsedSinceBootNanos()237 public long getElapsedSinceBootNanos() { 238 return SystemClock.elapsedRealtimeNanos(); 239 } 240 241 /** 242 * Is this a valid country code 243 * @param countryCode A 2-Character alphanumeric country code. 244 * @return true if the countryCode is valid, false otherwise. 245 */ isValidCountryCode(String countryCode)246 private static boolean isValidCountryCode(String countryCode) { 247 return countryCode != null && countryCode.length() == 2 248 && countryCode.chars().allMatch(Character::isLetterOrDigit); 249 } 250 251 /** 252 * Default country code stored in system property 253 * 254 * @return Country code if available, null otherwise. 255 */ getOemDefaultCountryCode()256 public String getOemDefaultCountryCode() { 257 String country = SystemProperties.get(BOOT_DEFAULT_UWB_COUNTRY_CODE); 258 return isValidCountryCode(country) ? country.toUpperCase(Locale.US) : null; 259 } 260 261 /** 262 * Helper method creating a context based on the app's uid (to deal with multi user scenarios) 263 */ 264 @Nullable createPackageContextAsUser(int uid)265 private Context createPackageContextAsUser(int uid) { 266 Context userContext; 267 try { 268 userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0, 269 UserHandle.getUserHandleForUid(uid)); 270 } catch (PackageManager.NameNotFoundException e) { 271 Log.e(TAG, "Unknown package name"); 272 return null; 273 } 274 if (userContext == null) { 275 Log.e(TAG, "Unable to retrieve user context for " + uid); 276 return null; 277 } 278 return userContext; 279 } 280 281 /** Helper method to check if the app is a system app. */ isSystemApp(int uid, @NonNull String packageName)282 public boolean isSystemApp(int uid, @NonNull String packageName) { 283 try { 284 ApplicationInfo info = createPackageContextAsUser(uid) 285 .getPackageManager() 286 .getApplicationInfo(packageName, 0); 287 return (info.flags & APP_INFO_FLAGS_SYSTEM_APP) != 0; 288 } catch (PackageManager.NameNotFoundException e) { 289 // In case of exception, assume unknown app (more strict checking) 290 // Note: This case will never happen since checkPackage is 291 // called to verify validity before checking App's version. 292 Log.e(TAG, "Failed to get the app info", e); 293 } 294 return false; 295 } 296 297 /** Helper method to retrieve app importance. */ getPackageImportance(int uid, @NonNull String packageName)298 private int getPackageImportance(int uid, @NonNull String packageName) { 299 try { 300 return createPackageContextAsUser(uid) 301 .getSystemService(ActivityManager.class) 302 .getPackageImportance(packageName); 303 } catch (SecurityException e) { 304 Log.e(TAG, "Failed to retrieve the app importance", e); 305 return ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; 306 } 307 } 308 309 /** Helper method to check if the app is from foreground app/service. */ isForegroundAppOrService(int uid, @NonNull String packageName)310 public boolean isForegroundAppOrService(int uid, @NonNull String packageName) { 311 try { 312 return getPackageImportance(uid, packageName) 313 <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 314 } catch (SecurityException e) { 315 Log.e(TAG, "Failed to retrieve the app importance", e); 316 return false; 317 } 318 } 319 } 320