• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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