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