1 /* 2 * Copyright (C) 2019 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.incident; 18 19 import android.app.AppOpsManager; 20 import android.app.BroadcastOptions; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.net.Uri; 27 import android.os.Handler; 28 import android.os.IIncidentAuthListener; 29 import android.os.IncidentManager; 30 import android.os.RemoteException; 31 import android.os.SystemClock; 32 import android.os.UserHandle; 33 import android.util.Log; 34 35 import java.io.FileDescriptor; 36 import java.io.PrintWriter; 37 import java.text.SimpleDateFormat; 38 import java.util.ArrayList; 39 import java.util.Date; 40 import java.util.Iterator; 41 import java.util.List; 42 43 // TODO: User changes should deny everything that's pending. 44 45 /** 46 * Tracker for reports pending approval. 47 */ 48 class PendingReports { 49 static final String TAG = IncidentCompanionService.TAG; 50 51 private final Handler mHandler = new Handler(); 52 private final RequestQueue mRequestQueue = new RequestQueue(mHandler); 53 private final Context mContext; 54 private final PackageManager mPackageManager; 55 private final AppOpsManager mAppOpsManager; 56 57 // 58 // All fields below must be protected by mLock 59 // 60 private final Object mLock = new Object(); 61 private final ArrayList<PendingReportRec> mPending = new ArrayList(); 62 63 /** 64 * The next ID we'll use when we make a PendingReportRec. 65 */ 66 private int mNextPendingId = 1; 67 68 /** 69 * One for each authorization that's pending. 70 */ 71 private final class PendingReportRec { 72 public int id; 73 public String callingPackage; 74 public int flags; 75 public IIncidentAuthListener listener; 76 public long addedRealtime; 77 public long addedWalltime; 78 public String receiverClass; 79 public String reportId; 80 81 /** 82 * Construct a PendingReportRec, with an auto-incremented id. 83 */ 84 PendingReportRec(String callingPackage, String receiverClass, String reportId, int flags, 85 IIncidentAuthListener listener) { 86 this.id = mNextPendingId++; 87 this.callingPackage = callingPackage; 88 this.flags = flags; 89 this.listener = listener; 90 this.addedRealtime = SystemClock.elapsedRealtime(); 91 this.addedWalltime = System.currentTimeMillis(); 92 this.receiverClass = receiverClass; 93 this.reportId = reportId; 94 } 95 96 /** 97 * Get the Uri that contains the flattened data. 98 */ 99 Uri getUri() { 100 final Uri.Builder builder = (new Uri.Builder()) 101 .scheme(IncidentManager.URI_SCHEME) 102 .authority(IncidentManager.URI_AUTHORITY) 103 .path(IncidentManager.URI_PATH) 104 .appendQueryParameter(IncidentManager.URI_PARAM_ID, Integer.toString(id)) 105 .appendQueryParameter(IncidentManager.URI_PARAM_CALLING_PACKAGE, callingPackage) 106 .appendQueryParameter(IncidentManager.URI_PARAM_FLAGS, Integer.toString(flags)) 107 .appendQueryParameter(IncidentManager.URI_PARAM_TIMESTAMP, 108 Long.toString(addedWalltime)); 109 if (receiverClass != null && receiverClass.length() > 0) { 110 builder.appendQueryParameter(IncidentManager.URI_PARAM_RECEIVER_CLASS, 111 receiverClass); 112 } 113 if (reportId != null && reportId.length() > 0) { 114 builder.appendQueryParameter(IncidentManager.URI_PARAM_REPORT_ID, reportId); 115 } 116 return builder.build(); 117 } 118 } 119 120 /** 121 * Construct new PendingReports with the context. 122 */ 123 PendingReports(Context context) { 124 mContext = context; 125 mPackageManager = context.getPackageManager(); 126 mAppOpsManager = context.getSystemService(AppOpsManager.class); 127 } 128 129 /** 130 * ONEWAY binder call to initiate authorizing the report. The actual logic is posted 131 * to mRequestQueue, and may happen later. 132 * <p> 133 * The security checks are handled by IncidentCompanionService. 134 */ 135 public void authorizeReport(int callingUid, final String callingPackage, 136 final String receiverClass, final String reportId, final int flags, 137 final IIncidentAuthListener listener) { 138 // Starting the system server is complicated, and rather than try to 139 // have a complicated lifecycle that we share with dumpstated and incidentd, 140 // we will accept the request, and then display it whenever it becomes possible to. 141 mRequestQueue.enqueue(listener.asBinder(), true, () -> { 142 authorizeReportImpl(callingUid, callingPackage, receiverClass, reportId, 143 flags, listener); 144 }); 145 } 146 147 /** 148 * ONEWAY binder call to cancel the inbound authorization request. 149 * <p> 150 * This is a oneway call, and so is authorizeReport, so the 151 * caller's ordering is preserved. The other calls on this object are synchronous, so 152 * their ordering is not guaranteed with respect to these calls. So the implementation 153 * sends out extra broadcasts to allow for eventual consistency. 154 * <p> 155 * The security checks are handled by IncidentCompanionService. 156 */ 157 public void cancelAuthorization(final IIncidentAuthListener listener) { 158 mRequestQueue.enqueue(listener.asBinder(), false, () -> { 159 cancelReportImpl(listener); 160 }); 161 } 162 163 /** 164 * SYNCHRONOUS binder call to get the list of reports that are pending confirmation 165 * by the user. 166 * <p> 167 * The security checks are handled by IncidentCompanionService. 168 */ 169 public List<String> getPendingReports() { 170 synchronized (mLock) { 171 final int size = mPending.size(); 172 final ArrayList<String> result = new ArrayList(size); 173 for (int i = 0; i < size; i++) { 174 result.add(mPending.get(i).getUri().toString()); 175 } 176 return result; 177 } 178 } 179 180 /** 181 * SYNCHRONOUS binder call to mark a report as approved. 182 * <p> 183 * The security checks are handled by IncidentCompanionService. 184 */ 185 public void approveReport(String uri) { 186 final PendingReportRec rec; 187 synchronized (mLock) { 188 rec = findAndRemovePendingReportRecLocked(uri); 189 if (rec == null) { 190 Log.e(TAG, "confirmApproved: Couldn't find record for uri: " + uri); 191 return; 192 } 193 } 194 195 // Re-do the broadcast, so whoever is listening knows the list changed, 196 // in case another one was added in the meantime. 197 sendBroadcast(); 198 199 Log.i(TAG, "Approved report: " + uri); 200 try { 201 rec.listener.onReportApproved(); 202 } catch (RemoteException ex) { 203 Log.w(TAG, "Failed calling back for approval for: " + uri, ex); 204 } 205 } 206 207 /** 208 * SYNCHRONOUS binder call to mark a report as NOT approved. 209 */ 210 public void denyReport(String uri) { 211 final PendingReportRec rec; 212 synchronized (mLock) { 213 rec = findAndRemovePendingReportRecLocked(uri); 214 if (rec == null) { 215 Log.e(TAG, "confirmDenied: Couldn't find record for uri: " + uri); 216 return; 217 } 218 } 219 220 // Re-do the broadcast, so whoever is listening knows the list changed, 221 // in case another one was added in the meantime. 222 sendBroadcast(); 223 224 Log.i(TAG, "Denied report: " + uri); 225 try { 226 rec.listener.onReportDenied(); 227 } catch (RemoteException ex) { 228 Log.w(TAG, "Failed calling back for denial for: " + uri, ex); 229 } 230 } 231 232 /** 233 * Implementation of adb shell dumpsys debugreportcompanion. 234 */ 235 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 236 if (args.length == 0) { 237 // Standard text dumpsys 238 final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 239 synchronized (mLock) { 240 final int size = mPending.size(); 241 writer.println("mPending: (" + size + ")"); 242 for (int i = 0; i < size; i++) { 243 final PendingReportRec entry = mPending.get(i); 244 writer.println(String.format(" %11d %s: %s", entry.addedRealtime, 245 df.format(new Date(entry.addedWalltime)), 246 entry.getUri().toString())); 247 } 248 } 249 } 250 } 251 252 /** 253 * Handle the boot process... Starts everything running once the system is 254 * up enough for us to do UI. 255 */ 256 public void onBootCompleted() { 257 // Release the enqueued work. 258 mRequestQueue.start(); 259 } 260 261 /** 262 * Start the confirmation process. 263 */ 264 private void authorizeReportImpl(int callingUid, final String callingPackage, 265 final String receiverClass, final String reportId, 266 int flags, final IIncidentAuthListener listener) { 267 // Enforce that the calling package pertains to the callingUid. 268 if (callingUid != 0 && !isPackageInUid(callingUid, callingPackage)) { 269 Log.w(TAG, "Calling uid " + callingUid + " doesn't match package " 270 + callingPackage); 271 denyReportBeforeAddingRec(listener, callingPackage); 272 return; 273 } 274 275 // Find the primary user of this device. 276 final int primaryUser = getAndValidateUser(); 277 if (primaryUser == UserHandle.USER_NULL) { 278 denyReportBeforeAddingRec(listener, callingPackage); 279 return; 280 } 281 282 // Find the approver app (hint: it's PermissionController). 283 final ComponentName receiver = getApproverComponent(primaryUser); 284 if (receiver == null) { 285 // We couldn't find an approver... so deny the request here and now, before we 286 // do anything else. 287 denyReportBeforeAddingRec(listener, callingPackage); 288 return; 289 } 290 291 // Save the record for when the PermissionController comes back to authorize it. 292 PendingReportRec rec = null; 293 synchronized (mLock) { 294 rec = new PendingReportRec(callingPackage, receiverClass, reportId, flags, listener); 295 mPending.add(rec); 296 } 297 298 try { 299 listener.asBinder().linkToDeath(() -> { 300 Log.i(TAG, "Got death notification listener=" + listener); 301 cancelReportImpl(listener, receiver, primaryUser); 302 }, 0); 303 } catch (RemoteException ex) { 304 Log.e(TAG, "Remote died while trying to register death listener: " + rec.getUri()); 305 // First, remove from our list. 306 cancelReportImpl(listener, receiver, primaryUser); 307 } 308 309 // Go tell Permission controller to start asking the user. 310 sendBroadcast(receiver, primaryUser); 311 } 312 313 /** 314 * Cancel a pending report request (because of an explicit call to cancel) 315 */ 316 private void cancelReportImpl(IIncidentAuthListener listener) { 317 final int primaryUser = getAndValidateUser(); 318 final ComponentName receiver = getApproverComponent(primaryUser); 319 if (primaryUser != UserHandle.USER_NULL && receiver != null) { 320 cancelReportImpl(listener, receiver, primaryUser); 321 } 322 } 323 324 /** 325 * Cancel a pending report request (either because of an explicit call to cancel 326 * by the calling app, or because of a binder death). 327 */ 328 private void cancelReportImpl(IIncidentAuthListener listener, ComponentName receiver, 329 int primaryUser) { 330 // First, remove from our list. 331 synchronized (mLock) { 332 removePendingReportRecLocked(listener); 333 } 334 // Second, call back to PermissionController to say it's canceled. 335 sendBroadcast(receiver, primaryUser); 336 } 337 338 /** 339 * Send an extra copy of the broadcast, to tell them that the list has changed 340 * because of an addition or removal. This function is less aggressive than 341 * authorizeReportImpl in logging about failures, because this is for use in 342 * cleanup cases to keep the apps' list in sync with ours. 343 */ 344 private void sendBroadcast() { 345 final int primaryUser = getAndValidateUser(); 346 if (primaryUser == UserHandle.USER_NULL) { 347 return; 348 } 349 final ComponentName receiver = getApproverComponent(primaryUser); 350 if (receiver == null) { 351 return; 352 } 353 sendBroadcast(receiver, primaryUser); 354 } 355 356 /** 357 * Send the confirmation broadcast. 358 */ 359 private void sendBroadcast(ComponentName receiver, int primaryUser) { 360 final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED); 361 intent.setComponent(receiver); 362 final BroadcastOptions options = BroadcastOptions.makeBasic(); 363 options.setBackgroundActivityStartsAllowed(true); 364 365 // Send it to the primary user. 366 mContext.sendBroadcastAsUser(intent, UserHandle.getUserHandleForUid(primaryUser), 367 android.Manifest.permission.APPROVE_INCIDENT_REPORTS, options.toBundle()); 368 } 369 370 /** 371 * Remove a PendingReportRec keyed by uri, and return it. 372 */ 373 private PendingReportRec findAndRemovePendingReportRecLocked(String uriString) { 374 final Uri uri = Uri.parse(uriString); 375 final int id; 376 try { 377 final String idStr = uri.getQueryParameter(IncidentManager.URI_PARAM_ID); 378 id = Integer.parseInt(idStr); 379 } catch (NumberFormatException ex) { 380 Log.w(TAG, "Can't parse id from: " + uriString); 381 return null; 382 } 383 384 for (Iterator<PendingReportRec> i = mPending.iterator(); i.hasNext();) { 385 final PendingReportRec rec = i.next(); 386 if (rec.id == id) { 387 i.remove(); 388 return rec; 389 } 390 } 391 return null; 392 } 393 394 /** 395 * Remove a PendingReportRec keyed by listener. 396 */ 397 private void removePendingReportRecLocked(IIncidentAuthListener listener) { 398 399 for (Iterator<PendingReportRec> i = mPending.iterator(); i.hasNext();) { 400 final PendingReportRec rec = i.next(); 401 if (rec.listener.asBinder() == listener.asBinder()) { 402 Log.i(TAG, " ...Removed PendingReportRec index=" + i + ": " + rec.getUri()); 403 i.remove(); 404 } 405 } 406 } 407 408 /** 409 * Just call listener.deny() (wrapping the RemoteException), without try to 410 * add it to the list. 411 */ 412 private void denyReportBeforeAddingRec(IIncidentAuthListener listener, String pkg) { 413 try { 414 listener.onReportDenied(); 415 } catch (RemoteException ex) { 416 Log.w(TAG, "Failed calling back for denial for " + pkg, ex); 417 } 418 } 419 420 /** 421 * Check whether the current user is the primary user, and return the user id if they are. 422 * Returns UserHandle.USER_NULL if not valid. 423 */ 424 private int getAndValidateUser() { 425 return IncidentCompanionService.getAndValidateUser(mContext); 426 } 427 428 /** 429 * Return the ComponentName of the BroadcastReceiver that will approve reports. 430 * The system must have zero or one of these installed. We only look on the 431 * system partition. When the broadcast happens, the component will also need 432 * have the APPROVE_INCIDENT_REPORTS permission. 433 */ 434 private ComponentName getApproverComponent(int userId) { 435 // Find the one true BroadcastReceiver 436 final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED); 437 final List<ResolveInfo> matches = mPackageManager.queryBroadcastReceiversAsUser(intent, 438 PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE 439 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); 440 if (matches.size() == 1) { 441 return matches.get(0).getComponentInfo().getComponentName(); 442 } else { 443 Log.w(TAG, "Didn't find exactly one BroadcastReceiver to handle " 444 + Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED 445 + ". The report will be denied. size=" 446 + matches.size() + ": matches=" + matches); 447 return null; 448 } 449 } 450 451 /** 452 * Return whether the package is one of the packages installed for the uid. 453 */ 454 private boolean isPackageInUid(int uid, String packageName) { 455 try { 456 mAppOpsManager.checkPackage(uid, packageName); 457 return true; 458 } catch (SecurityException ex) { 459 return false; 460 } 461 } 462 } 463 464