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.ambientcontext; 18 19 import android.annotation.NonNull; 20 import android.annotation.UserIdInt; 21 import android.app.ActivityManager; 22 import android.app.ActivityOptions; 23 import android.app.ActivityTaskManager; 24 import android.app.AppGlobals; 25 import android.app.BroadcastOptions; 26 import android.app.PendingIntent; 27 import android.app.ambientcontext.AmbientContextEvent; 28 import android.app.ambientcontext.AmbientContextEventRequest; 29 import android.app.ambientcontext.AmbientContextManager; 30 import android.app.ambientcontext.IAmbientContextObserver; 31 import android.content.ActivityNotFoundException; 32 import android.content.ComponentName; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.pm.PackageManager; 36 import android.content.pm.ParceledListSlice; 37 import android.content.pm.ServiceInfo; 38 import android.os.Binder; 39 import android.os.Bundle; 40 import android.os.RemoteCallback; 41 import android.os.RemoteException; 42 import android.service.ambientcontext.AmbientContextDetectionResult; 43 import android.service.ambientcontext.AmbientContextDetectionServiceStatus; 44 import android.text.TextUtils; 45 import android.util.IndentingPrintWriter; 46 import android.util.Slog; 47 48 import com.android.internal.annotations.GuardedBy; 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.server.infra.AbstractPerUserSystemService; 51 52 import java.io.PrintWriter; 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.List; 56 import java.util.function.Consumer; 57 58 /** 59 * Base per-user manager service for {@link AmbientContextEvent}s. 60 */ 61 abstract class AmbientContextManagerPerUserService extends 62 AbstractPerUserSystemService<AmbientContextManagerPerUserService, 63 AmbientContextManagerService> { 64 private static final String TAG = 65 AmbientContextManagerPerUserService.class.getSimpleName(); 66 67 /** 68 * The type of service. 69 */ 70 enum ServiceType { 71 DEFAULT, 72 WEARABLE 73 } 74 AmbientContextManagerPerUserService( @onNull AmbientContextManagerService master, Object lock, @UserIdInt int userId)75 AmbientContextManagerPerUserService( 76 @NonNull AmbientContextManagerService master, Object lock, @UserIdInt int userId) { 77 super(master, lock, userId); 78 } 79 80 /** 81 * Returns the current bound AmbientContextManagerPerUserService component for this user. 82 */ getComponentName()83 abstract ComponentName getComponentName(); 84 85 /** 86 * Sets the component name for the per user service. 87 */ setComponentName(ComponentName componentName)88 abstract void setComponentName(ComponentName componentName); 89 90 /** 91 * Ensures that the remote service is initiated. 92 */ ensureRemoteServiceInitiated()93 abstract void ensureRemoteServiceInitiated(); 94 95 /** 96 * Returns the AmbientContextManagerPerUserService {@link ServiceType} for this user. 97 */ getServiceType()98 abstract ServiceType getServiceType(); 99 100 /** 101 * Returns the int config for the consent component for the 102 * specific AmbientContextManagerPerUserService type 103 */ getConsentComponentConfig()104 abstract int getConsentComponentConfig(); 105 106 /** 107 * Returns the int config for the intent extra key for the 108 * caller's package name while requesting ambient context consent. 109 */ getAmbientContextPackageNameExtraKeyConfig()110 abstract int getAmbientContextPackageNameExtraKeyConfig(); 111 112 /** 113 * Returns the int config for the Intent extra key for the event code int array while 114 * requesting ambient context consent. 115 */ getAmbientContextEventArrayExtraKeyConfig()116 abstract int getAmbientContextEventArrayExtraKeyConfig(); 117 118 /** 119 * Returns the permission that is required to bind to this service. 120 */ getProtectedBindPermission()121 abstract String getProtectedBindPermission(); 122 123 /** 124 * Returns the remote service implementation for this user. 125 */ getRemoteService()126 abstract RemoteAmbientDetectionService getRemoteService(); 127 128 /** 129 * Clears the remote service. 130 */ clearRemoteService()131 abstract void clearRemoteService(); 132 133 /** 134 * Called when there's an application with the callingPackage name is requesting for 135 * the AmbientContextDetection's service status. 136 * 137 * @param eventTypes the event types to query for 138 * @param callingPackage the package query for information 139 * @param statusCallback the callback to deliver the status on 140 */ onQueryServiceStatus(int[] eventTypes, String callingPackage, RemoteCallback statusCallback)141 public void onQueryServiceStatus(int[] eventTypes, String callingPackage, 142 RemoteCallback statusCallback) { 143 Slog.d(TAG, "Query event status of " + Arrays.toString(eventTypes) 144 + " for " + callingPackage); 145 synchronized (mLock) { 146 if (!setUpServiceIfNeeded()) { 147 Slog.w(TAG, "Detection service is not available at this moment."); 148 sendStatusCallback(statusCallback, 149 AmbientContextManager.STATUS_SERVICE_UNAVAILABLE); 150 return; 151 } 152 ensureRemoteServiceInitiated(); 153 getRemoteService().queryServiceStatus( 154 eventTypes, 155 callingPackage, 156 getServerStatusCallback( 157 statusCode -> sendStatusCallback(statusCallback, statusCode))); 158 } 159 } 160 161 /** 162 * Unregisters the client from all previously registered events by removing from the 163 * mExistingRequests map, and unregister events from the service if those events are not 164 * requested by other apps. 165 */ onUnregisterObserver(String callingPackage)166 public void onUnregisterObserver(String callingPackage) { 167 synchronized (mLock) { 168 stopDetection(callingPackage); 169 mMaster.clientRemoved(mUserId, callingPackage); 170 } 171 } 172 173 /** 174 * Starts the consent activity for the calling package and event types. 175 */ onStartConsentActivity(int[] eventTypes, String callingPackage)176 public void onStartConsentActivity(int[] eventTypes, String callingPackage) { 177 Slog.d(TAG, "Opening consent activity of " + Arrays.toString(eventTypes) 178 + " for " + callingPackage); 179 180 // Look up the recent task from the callingPackage 181 ActivityManager.RecentTaskInfo task; 182 ParceledListSlice<ActivityManager.RecentTaskInfo> recentTasks; 183 int userId = getUserId(); 184 try { 185 recentTasks = ActivityTaskManager.getService().getRecentTasks(/*maxNum*/1, 186 /*flags*/ 0, userId); 187 } catch (RemoteException e) { 188 Slog.e(TAG, "Failed to query recent tasks!"); 189 return; 190 } 191 192 if ((recentTasks == null) || recentTasks.getList().isEmpty()) { 193 Slog.e(TAG, "Recent task list is empty!"); 194 return; 195 } 196 197 task = recentTasks.getList().get(0); 198 if (!callingPackage.equals(task.topActivityInfo.packageName)) { 199 Slog.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName 200 + " doesn't match with client package name: " + callingPackage); 201 return; 202 } 203 204 // Start activity as the same task from the callingPackage 205 ComponentName consentComponent = getConsentComponent(); 206 if (consentComponent == null) { 207 Slog.e(TAG, "Consent component not found!"); 208 return; 209 } 210 211 Slog.d(TAG, "Starting consent activity for " + callingPackage); 212 Intent intent = new Intent(); 213 final long identity = Binder.clearCallingIdentity(); 214 try { 215 Context context = getContext(); 216 String packageNameExtraKey = context.getResources().getString( 217 getAmbientContextPackageNameExtraKeyConfig()); 218 String eventArrayExtraKey = context.getResources().getString( 219 getAmbientContextEventArrayExtraKeyConfig()); 220 221 // Create consent activity intent with the calling package name and requested events 222 intent.setComponent(consentComponent); 223 if (packageNameExtraKey != null) { 224 intent.putExtra(packageNameExtraKey, callingPackage); 225 } else { 226 Slog.d(TAG, "Missing packageNameExtraKey for consent activity"); 227 } 228 if (eventArrayExtraKey != null) { 229 intent.putExtra(eventArrayExtraKey, eventTypes); 230 } else { 231 Slog.d(TAG, "Missing eventArrayExtraKey for consent activity"); 232 } 233 234 // Set parent to the calling app's task 235 ActivityOptions options = ActivityOptions.makeBasic(); 236 options.setLaunchTaskId(task.taskId); 237 context.startActivityAsUser(intent, options.toBundle(), context.getUser()); 238 } catch (ActivityNotFoundException e) { 239 Slog.e(TAG, "unable to start consent activity"); 240 } finally { 241 Binder.restoreCallingIdentity(identity); 242 } 243 } 244 245 /** 246 * Handles client registering as an observer. Only one registration is supported per app 247 * package. A new registration from the same package will overwrite the previous registration. 248 */ onRegisterObserver(AmbientContextEventRequest request, String packageName, IAmbientContextObserver observer)249 public void onRegisterObserver(AmbientContextEventRequest request, 250 String packageName, IAmbientContextObserver observer) { 251 synchronized (mLock) { 252 if (!setUpServiceIfNeeded()) { 253 Slog.w(TAG, "Detection service is not available at this moment."); 254 completeRegistration(observer, AmbientContextManager.STATUS_SERVICE_UNAVAILABLE); 255 return; 256 } 257 258 // Register package and add to existing ClientRequests cache 259 startDetection(request, packageName, observer); 260 mMaster.newClientAdded(mUserId, request, packageName, observer); 261 } 262 } 263 264 @Override newServiceInfoLocked(@onNull ComponentName serviceComponent)265 protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) 266 throws PackageManager.NameNotFoundException { 267 Slog.d(TAG, "newServiceInfoLocked with component name: " 268 + serviceComponent.getClassName()); 269 270 if (getComponentName() == null 271 || !serviceComponent.getClassName().equals(getComponentName().getClassName())) { 272 Slog.d(TAG, "service name does not match this per user, returning..."); 273 return null; 274 } 275 276 ServiceInfo serviceInfo; 277 try { 278 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 279 0, mUserId); 280 if (serviceInfo != null) { 281 final String permission = serviceInfo.permission; 282 if (!getProtectedBindPermission().equals( 283 permission)) { 284 throw new SecurityException(String.format( 285 "Service %s requires %s permission. Found %s permission", 286 serviceInfo.getComponentName(), 287 getProtectedBindPermission(), 288 serviceInfo.permission)); 289 } 290 } 291 } catch (RemoteException e) { 292 throw new PackageManager.NameNotFoundException( 293 "Could not get service for " + serviceComponent); 294 } 295 return serviceInfo; 296 } 297 298 /** 299 * Dumps the remote service. 300 */ dumpLocked(@onNull String prefix, @NonNull PrintWriter pw)301 protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) { 302 synchronized (super.mLock) { 303 super.dumpLocked(prefix, pw); 304 } 305 RemoteAmbientDetectionService remoteService = getRemoteService(); 306 if (remoteService != null) { 307 remoteService.dump("", new IndentingPrintWriter(pw, " ")); 308 } 309 } 310 311 /** 312 * Send request to the remote AmbientContextDetectionService impl to stop detecting the 313 * specified events. Intended for use by shell command for testing. 314 * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission. 315 */ 316 @VisibleForTesting stopDetection(String packageName)317 protected void stopDetection(String packageName) { 318 Slog.d(TAG, "Stop detection for " + packageName); 319 synchronized (mLock) { 320 if (getComponentName() != null) { 321 ensureRemoteServiceInitiated(); 322 RemoteAmbientDetectionService remoteService = getRemoteService(); 323 remoteService.stopDetection(packageName); 324 } 325 } 326 } 327 328 /** 329 * Destroys this service and unbinds from the remote service. 330 */ destroyLocked()331 protected void destroyLocked() { 332 Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed."); 333 RemoteAmbientDetectionService remoteService = getRemoteService(); 334 if (remoteService != null) { 335 synchronized (mLock) { 336 remoteService.unbind(); 337 clearRemoteService(); 338 } 339 } 340 } 341 342 /** 343 * Send request to the remote AmbientContextDetectionService impl to start detecting the 344 * specified events. Intended for use by shell command for testing. 345 * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission. 346 */ startDetection(AmbientContextEventRequest request, String callingPackage, IAmbientContextObserver observer)347 protected void startDetection(AmbientContextEventRequest request, String callingPackage, 348 IAmbientContextObserver observer) { 349 Slog.d(TAG, "Requested detection of " + request.getEventTypes()); 350 synchronized (mLock) { 351 if (setUpServiceIfNeeded()) { 352 ensureRemoteServiceInitiated(); 353 RemoteAmbientDetectionService remoteService = getRemoteService(); 354 remoteService.startDetection(request, callingPackage, 355 createDetectionResultRemoteCallback(), 356 getServerStatusCallback( 357 statusCode -> completeRegistration(observer, statusCode))); 358 } else { 359 Slog.w(TAG, "No valid component found for AmbientContextDetectionService"); 360 completeRegistration(observer, 361 AmbientContextManager.STATUS_NOT_SUPPORTED); 362 } 363 } 364 } 365 366 /** 367 * Notifies the observer the status of the registration. 368 * 369 * @param observer the observer to notify 370 * @param statusCode the status to notify 371 */ completeRegistration(IAmbientContextObserver observer, int statusCode)372 protected void completeRegistration(IAmbientContextObserver observer, int statusCode) { 373 try { 374 observer.onRegistrationComplete(statusCode); 375 } catch (RemoteException e) { 376 Slog.w(TAG, "Failed to call IAmbientContextObserver.onRegistrationComplete: " 377 + e.getMessage()); 378 } 379 } 380 381 /** 382 * Sends the status on the {@link RemoteCallback}. 383 * 384 * @param statusCallback the callback to send the status on 385 * @param statusCode the status to send 386 */ sendStatusCallback(RemoteCallback statusCallback, @AmbientContextManager.StatusCode int statusCode)387 protected void sendStatusCallback(RemoteCallback statusCallback, 388 @AmbientContextManager.StatusCode int statusCode) { 389 Bundle bundle = new Bundle(); 390 bundle.putInt( 391 AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY, 392 statusCode); 393 statusCallback.sendResult(bundle); 394 } 395 396 /** 397 * Sends out the Intent to the client after the event is detected. 398 * 399 * @param pendingIntent Client's PendingIntent for callback 400 * @param events detected events from the detection service 401 */ sendDetectionResultIntent(PendingIntent pendingIntent, List<AmbientContextEvent> events)402 protected void sendDetectionResultIntent(PendingIntent pendingIntent, 403 List<AmbientContextEvent> events) { 404 Intent intent = new Intent(); 405 intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS, 406 new ArrayList(events)); 407 // Explicitly disallow the receiver from starting activities, to prevent apps from utilizing 408 // the PendingIntent as a backdoor to do this. 409 BroadcastOptions options = BroadcastOptions.makeBasic(); 410 options.setPendingIntentBackgroundActivityLaunchAllowed(false); 411 try { 412 pendingIntent.send(getContext(), 0, intent, null, 413 null, null, options.toBundle()); 414 Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": " 415 + events); 416 } catch (PendingIntent.CanceledException e) { 417 Slog.w(TAG, "Couldn't deliver pendingIntent:" + pendingIntent); 418 } 419 } 420 421 @NonNull createDetectionResultRemoteCallback()422 protected RemoteCallback createDetectionResultRemoteCallback() { 423 return new RemoteCallback(result -> { 424 AmbientContextDetectionResult detectionResult = 425 (AmbientContextDetectionResult) result.get( 426 AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY); 427 String packageName = detectionResult.getPackageName(); 428 IAmbientContextObserver observer = mMaster.getClientRequestObserver( 429 mUserId, packageName); 430 if (observer == null) { 431 return; 432 } 433 434 final long token = Binder.clearCallingIdentity(); 435 try { 436 observer.onEvents(detectionResult.getEvents()); 437 Slog.i(TAG, "Got detection result of " + detectionResult.getEvents() 438 + " for " + packageName); 439 } catch (RemoteException e) { 440 Slog.w(TAG, "Failed to call IAmbientContextObserver.onEvents: " + e.getMessage()); 441 } finally { 442 Binder.restoreCallingIdentity(token); 443 } 444 }); 445 } 446 447 /** 448 * Resolves and sets up the service if it had not been done yet. Returns true if the service 449 * is available. 450 */ 451 @GuardedBy("mLock") 452 @VisibleForTesting setUpServiceIfNeeded()453 private boolean setUpServiceIfNeeded() { 454 if (getComponentName() == null) { 455 ComponentName[] componentNames = updateServiceInfoListLocked(); 456 if (componentNames == null || componentNames.length != 2) { 457 Slog.d(TAG, "updateServiceInfoListLocked returned incorrect componentNames"); 458 return false; 459 } 460 461 switch (getServiceType()) { 462 case DEFAULT: 463 setComponentName(componentNames[0]); 464 break; 465 case WEARABLE: 466 setComponentName(componentNames[1]); 467 break; 468 default: 469 Slog.d(TAG, "updateServiceInfoListLocked returned unknown service types."); 470 return false; 471 } 472 } 473 474 if (getComponentName() == null) { 475 return false; 476 } 477 478 ServiceInfo serviceInfo; 479 try { 480 serviceInfo = AppGlobals.getPackageManager().getServiceInfo( 481 getComponentName(), 0, mUserId); 482 } catch (RemoteException e) { 483 Slog.w(TAG, "RemoteException while setting up service"); 484 return false; 485 } 486 return serviceInfo != null; 487 } 488 489 /** 490 * Returns a RemoteCallback that handles the status from the detection service, and 491 * sends results to the client callback. 492 */ getServerStatusCallback(Consumer<Integer> statusConsumer)493 private RemoteCallback getServerStatusCallback(Consumer<Integer> statusConsumer) { 494 return new RemoteCallback(result -> { 495 AmbientContextDetectionServiceStatus serviceStatus = 496 (AmbientContextDetectionServiceStatus) result.get( 497 AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY); 498 final long token = Binder.clearCallingIdentity(); 499 try { 500 int statusCode = serviceStatus.getStatusCode(); 501 statusConsumer.accept(statusCode); 502 Slog.i(TAG, "Got detection status of " + statusCode 503 + " for " + serviceStatus.getPackageName()); 504 } finally { 505 Binder.restoreCallingIdentity(token); 506 } 507 }); 508 } 509 510 /** 511 * Returns the consent activity component from config lookup. 512 */ 513 private ComponentName getConsentComponent() { 514 Context context = getContext(); 515 String consentComponent = context.getResources().getString(getConsentComponentConfig()); 516 if (TextUtils.isEmpty(consentComponent)) { 517 return null; 518 } 519 Slog.i(TAG, "Consent component name: " + consentComponent); 520 return ComponentName.unflattenFromString(consentComponent); 521 } 522 } 523