1 /* 2 * Copyright (C) 2016 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 android.os; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.annotation.SystemService; 26 import android.content.Context; 27 import android.net.Uri; 28 import android.util.Slog; 29 30 import java.io.Closeable; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.OutputStream; 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Objects; 39 import java.util.concurrent.Executor; 40 41 /** 42 * Class to take an incident report. 43 * 44 * @hide 45 */ 46 @SystemApi 47 @SystemService(Context.INCIDENT_SERVICE) 48 public class IncidentManager { 49 private static final String TAG = "IncidentManager"; 50 51 /** 52 * Authority for pending report id urls. 53 * 54 * @hide 55 */ 56 public static final String URI_SCHEME = "content"; 57 58 /** 59 * Authority for pending report id urls. 60 * 61 * @hide 62 */ 63 public static final String URI_AUTHORITY = "android.os.IncidentManager"; 64 65 /** 66 * Authority for pending report id urls. 67 * 68 * @hide 69 */ 70 public static final String URI_PATH = "/pending"; 71 72 /** 73 * Query parameter for the uris for the pending report id. 74 * 75 * @hide 76 */ 77 public static final String URI_PARAM_ID = "id"; 78 79 /** 80 * Query parameter for the uris for the incident report id. 81 * 82 * @hide 83 */ 84 public static final String URI_PARAM_REPORT_ID = "r"; 85 86 /** 87 * Query parameter for the uris for the pending report id. 88 * 89 * @hide 90 */ 91 public static final String URI_PARAM_CALLING_PACKAGE = "pkg"; 92 93 /** 94 * Query parameter for the uris for the pending report id, in wall clock 95 * ({@link System.currentTimeMillis()}) timebase. 96 * 97 * @hide 98 */ 99 public static final String URI_PARAM_TIMESTAMP = "t"; 100 101 /** 102 * Query parameter for the uris for the pending report id. 103 * 104 * @hide 105 */ 106 public static final String URI_PARAM_FLAGS = "flags"; 107 108 /** 109 * Query parameter for the uris for the pending report id. 110 * 111 * @hide 112 */ 113 public static final String URI_PARAM_RECEIVER_CLASS = "receiver"; 114 115 /** 116 * Do the confirmation with a dialog instead of the default, which is a notification. 117 * It is possible for the dialog to be downgraded to a notification in some cases. 118 */ 119 public static final int FLAG_CONFIRMATION_DIALOG = 0x1; 120 121 /** 122 * Flag marking whether corresponding pending report allows consentless bugreport. 123 */ 124 public static final int FLAG_ALLOW_CONSENTLESS_BUGREPORT = 0x2; 125 126 /** @hide */ 127 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 128 FLAG_CONFIRMATION_DIALOG, 129 FLAG_ALLOW_CONSENTLESS_BUGREPORT, 130 }) 131 @Retention(RetentionPolicy.SOURCE) 132 public @interface PendingReportFlags {} 133 134 /** 135 * Flag marking fields and incident reports than can be taken 136 * off the device only via adb. 137 */ 138 public static final int PRIVACY_POLICY_LOCAL = 0; 139 140 /** 141 * Flag marking fields and incident reports than can be taken 142 * off the device with contemporary consent. 143 */ 144 public static final int PRIVACY_POLICY_EXPLICIT = 100; 145 146 /** 147 * Flag marking fields and incident reports than can be taken 148 * off the device with prior consent. 149 */ 150 public static final int PRIVACY_POLICY_AUTO = 200; 151 152 /** @hide */ 153 @IntDef(flag = false, prefix = { "PRIVACY_POLICY_" }, value = { 154 PRIVACY_POLICY_AUTO, 155 PRIVACY_POLICY_EXPLICIT, 156 PRIVACY_POLICY_LOCAL, 157 }) 158 @Retention(RetentionPolicy.SOURCE) 159 public @interface PrivacyPolicy {} 160 161 private final Context mContext; 162 163 private Object mLock = new Object(); 164 private IIncidentManager mIncidentService; 165 private IIncidentCompanion mCompanionService; 166 167 /** 168 * Record for a report that has been taken and is pending user authorization 169 * to share it. 170 * @hide 171 */ 172 @SystemApi 173 public static class PendingReport { 174 /** 175 * Encoded data. 176 */ 177 private final Uri mUri; 178 179 /** 180 * URI_PARAM_FLAGS from the uri 181 */ 182 private final int mFlags; 183 184 /** 185 * URI_PARAM_CALLING_PACKAGE from the uri 186 */ 187 private final String mRequestingPackage; 188 189 /** 190 * URI_PARAM_TIMESTAMP from the uri 191 */ 192 private final long mTimestamp; 193 194 /** 195 * Constructor. 196 */ PendingReport(@onNull Uri uri)197 public PendingReport(@NonNull Uri uri) { 198 int flags = 0; 199 try { 200 flags = Integer.parseInt(uri.getQueryParameter(URI_PARAM_FLAGS)); 201 } catch (NumberFormatException ex) { 202 throw new RuntimeException("Invalid URI: No " + URI_PARAM_FLAGS 203 + " parameter. " + uri); 204 } 205 mFlags = flags; 206 207 String requestingPackage = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE); 208 if (requestingPackage == null) { 209 throw new RuntimeException("Invalid URI: No " + URI_PARAM_CALLING_PACKAGE 210 + " parameter. " + uri); 211 } 212 mRequestingPackage = requestingPackage; 213 214 long timestamp = -1; 215 try { 216 timestamp = Long.parseLong(uri.getQueryParameter(URI_PARAM_TIMESTAMP)); 217 } catch (NumberFormatException ex) { 218 throw new RuntimeException("Invalid URI: No " + URI_PARAM_TIMESTAMP 219 + " parameter. " + uri); 220 } 221 mTimestamp = timestamp; 222 223 mUri = uri; 224 } 225 226 /** 227 * Get the package with which this report will be shared. 228 */ getRequestingPackage()229 public @NonNull String getRequestingPackage() { 230 return mRequestingPackage; 231 } 232 233 /** 234 * Get the flags requested for this pending report. 235 * 236 * @see PendingReportFlags 237 */ 238 @PendingReportFlags getFlags()239 public int getFlags() { 240 return mFlags; 241 } 242 243 /** 244 * Get the time this pending report was posted. 245 */ getTimestamp()246 public long getTimestamp() { 247 return mTimestamp; 248 } 249 250 /** 251 * Get the URI associated with this PendingReport. It can be used to 252 * re-retrieve it from {@link IncidentManager} or set as the data field of 253 * an Intent. 254 */ getUri()255 public @NonNull Uri getUri() { 256 return mUri; 257 } 258 259 /** 260 * String representation of this PendingReport. 261 */ 262 @Override toString()263 public @NonNull String toString() { 264 return "PendingReport(" + getUri().toString() + ")"; 265 } 266 267 /** 268 * @inheritDoc 269 */ 270 @Override equals(@ullable Object obj)271 public boolean equals(@Nullable Object obj) { 272 if (this == obj) { 273 return true; 274 } 275 if (!(obj instanceof PendingReport)) { 276 return false; 277 } 278 final PendingReport that = (PendingReport) obj; 279 return this.mUri.equals(that.mUri) 280 && this.mFlags == that.mFlags 281 && this.mRequestingPackage.equals(that.mRequestingPackage) 282 && this.mTimestamp == that.mTimestamp; 283 } 284 } 285 286 /** 287 * Record of an incident report that has previously been taken. 288 * @hide 289 */ 290 @SystemApi 291 public static class IncidentReport implements Parcelable, Closeable { 292 private final long mTimestampNs; 293 private final int mPrivacyPolicy; 294 private ParcelFileDescriptor mFileDescriptor; 295 IncidentReport(Parcel in)296 public IncidentReport(Parcel in) { 297 mTimestampNs = in.readLong(); 298 mPrivacyPolicy = in.readInt(); 299 if (in.readInt() != 0) { 300 mFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in); 301 } else { 302 mFileDescriptor = null; 303 } 304 } 305 306 /** 307 * Close the input stream associated with this entry. 308 */ close()309 public void close() { 310 try { 311 if (mFileDescriptor != null) { 312 mFileDescriptor.close(); 313 mFileDescriptor = null; 314 } 315 } catch (IOException e) { 316 } 317 } 318 319 /** 320 * Get the time at which this incident report was taken, in wall clock time 321 * ({@link System#currenttimeMillis System.currenttimeMillis()} time base). 322 */ getTimestamp()323 public long getTimestamp() { 324 return mTimestampNs / 1000000; 325 } 326 327 /** 328 * Get the privacy level to which this report has been filtered. 329 * 330 * @see #PRIVACY_POLICY_AUTO 331 * @see #PRIVACY_POLICY_EXPLICIT 332 * @see #PRIVACY_POLICY_LOCAL 333 */ getPrivacyPolicy()334 public long getPrivacyPolicy() { 335 return mPrivacyPolicy; 336 } 337 338 /** 339 * Get the contents of this incident report. 340 */ getInputStream()341 public InputStream getInputStream() throws IOException { 342 if (mFileDescriptor == null) { 343 return null; 344 } 345 return new ParcelFileDescriptor.AutoCloseInputStream(mFileDescriptor); 346 } 347 348 /** 349 * @inheritDoc 350 */ describeContents()351 public int describeContents() { 352 return mFileDescriptor != null ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0; 353 } 354 355 /** 356 * @inheritDoc 357 */ writeToParcel(Parcel out, int flags)358 public void writeToParcel(Parcel out, int flags) { 359 out.writeLong(mTimestampNs); 360 out.writeInt(mPrivacyPolicy); 361 if (mFileDescriptor != null) { 362 out.writeInt(1); 363 mFileDescriptor.writeToParcel(out, flags); 364 } else { 365 out.writeInt(0); 366 } 367 } 368 369 /** 370 * {@link Parcelable.Creator Creator} for {@link IncidentReport}. 371 */ 372 public static final @android.annotation.NonNull Parcelable.Creator<IncidentReport> CREATOR = new Parcelable.Creator() { 373 /** 374 * @inheritDoc 375 */ 376 public IncidentReport[] newArray(int size) { 377 return new IncidentReport[size]; 378 } 379 380 /** 381 * @inheritDoc 382 */ 383 public IncidentReport createFromParcel(Parcel in) { 384 return new IncidentReport(in); 385 } 386 }; 387 } 388 389 /** 390 * Listener for the status of an incident report being authorized or denied. 391 * 392 * @see #requestAuthorization 393 * @see #cancelAuthorization 394 */ 395 public static class AuthListener { 396 Executor mExecutor; 397 398 IIncidentAuthListener.Stub mBinder = new IIncidentAuthListener.Stub() { 399 @Override 400 public void onReportApproved() { 401 if (mExecutor != null) { 402 mExecutor.execute(() -> { 403 AuthListener.this.onReportApproved(); 404 }); 405 } else { 406 AuthListener.this.onReportApproved(); 407 } 408 } 409 410 @Override 411 public void onReportDenied() { 412 if (mExecutor != null) { 413 mExecutor.execute(() -> { 414 AuthListener.this.onReportDenied(); 415 }); 416 } else { 417 AuthListener.this.onReportDenied(); 418 } 419 } 420 }; 421 422 /** 423 * Called when a report is approved. 424 */ onReportApproved()425 public void onReportApproved() { 426 } 427 428 /** 429 * Called when a report is denied. 430 */ onReportDenied()431 public void onReportDenied() { 432 } 433 } 434 435 /** 436 * Callback for dumping an extended (usually vendor-supplied) incident report section 437 * 438 * @see #registerSection 439 * @see #unregisterSection 440 */ 441 public static class DumpCallback { 442 private int mId; 443 private Executor mExecutor; 444 445 IIncidentDumpCallback.Stub mBinder = new IIncidentDumpCallback.Stub() { 446 @Override 447 public void onDumpSection(ParcelFileDescriptor pfd) { 448 if (mExecutor != null) { 449 mExecutor.execute(() -> { 450 DumpCallback.this.onDumpSection(mId, 451 new ParcelFileDescriptor.AutoCloseOutputStream(pfd)); 452 }); 453 } else { 454 DumpCallback.this.onDumpSection(mId, 455 new ParcelFileDescriptor.AutoCloseOutputStream(pfd)); 456 } 457 } 458 }; 459 460 /** 461 * Dump the registered section as a protobuf message to the given OutputStream. Called when 462 * incidentd requests to dump this section. 463 * 464 * @param id the id of the registered section. The same id used in calling 465 * {@link #registerSection(int, String, DumpCallback)} will be passed in here. 466 * @param out the OutputStream to write the protobuf message 467 */ onDumpSection(int id, @NonNull OutputStream out)468 public void onDumpSection(int id, @NonNull OutputStream out) { 469 } 470 } 471 472 /** 473 * @hide 474 */ IncidentManager(Context context)475 public IncidentManager(Context context) { 476 mContext = context; 477 } 478 479 /** 480 * Take an incident report. 481 */ 482 @RequiresPermission(allOf = { 483 android.Manifest.permission.DUMP, 484 android.Manifest.permission.PACKAGE_USAGE_STATS 485 }) reportIncident(IncidentReportArgs args)486 public void reportIncident(IncidentReportArgs args) { 487 reportIncidentInternal(args); 488 } 489 490 /** 491 * Request authorization of an incident report. 492 */ 493 @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL) requestAuthorization(int callingUid, String callingPackage, int flags, AuthListener listener)494 public void requestAuthorization(int callingUid, String callingPackage, int flags, 495 AuthListener listener) { 496 requestAuthorization(callingUid, callingPackage, flags, 497 mContext.getMainExecutor(), listener); 498 } 499 500 /** 501 * Request authorization of an incident report. 502 * @hide 503 */ 504 @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL) requestAuthorization(int callingUid, @NonNull String callingPackage, int flags, @NonNull @CallbackExecutor Executor executor, @NonNull AuthListener listener)505 public void requestAuthorization(int callingUid, @NonNull String callingPackage, int flags, 506 @NonNull @CallbackExecutor Executor executor, @NonNull AuthListener listener) { 507 try { 508 if (listener.mExecutor != null) { 509 throw new RuntimeException("Do not reuse AuthListener objects when calling" 510 + " requestAuthorization"); 511 } 512 listener.mExecutor = executor; 513 getCompanionServiceLocked().authorizeReport(callingUid, callingPackage, null, null, 514 flags, listener.mBinder); 515 } catch (RemoteException ex) { 516 // System process going down 517 throw new RuntimeException(ex); 518 } 519 } 520 521 /** 522 * Cancel a previous request for incident report authorization. 523 */ 524 @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL) cancelAuthorization(AuthListener listener)525 public void cancelAuthorization(AuthListener listener) { 526 try { 527 getCompanionServiceLocked().cancelAuthorization(listener.mBinder); 528 } catch (RemoteException ex) { 529 // System process going down 530 throw new RuntimeException(ex); 531 } 532 } 533 534 /** 535 * Get incident (and bug) reports that are pending approval to share. 536 */ 537 @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) getPendingReports()538 public List<PendingReport> getPendingReports() { 539 List<String> strings; 540 try { 541 strings = getCompanionServiceLocked().getPendingReports(); 542 } catch (RemoteException ex) { 543 throw new RuntimeException(ex); 544 } 545 final int size = strings.size(); 546 ArrayList<PendingReport> result = new ArrayList(size); 547 for (int i = 0; i < size; i++) { 548 result.add(new PendingReport(Uri.parse(strings.get(i)))); 549 } 550 return result; 551 } 552 553 /** 554 * Allow this report to be shared with the given app. 555 */ 556 @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) approveReport(Uri uri)557 public void approveReport(Uri uri) { 558 try { 559 getCompanionServiceLocked().approveReport(uri.toString()); 560 } catch (RemoteException ex) { 561 // System process going down 562 throw new RuntimeException(ex); 563 } 564 } 565 566 /** 567 * Do not allow this report to be shared with the given app. 568 */ 569 @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) denyReport(Uri uri)570 public void denyReport(Uri uri) { 571 try { 572 getCompanionServiceLocked().denyReport(uri.toString()); 573 } catch (RemoteException ex) { 574 // System process going down 575 throw new RuntimeException(ex); 576 } 577 } 578 579 /** 580 * Register a callback to dump an extended incident report section with the given id and name, 581 * running on the supplied executor. 582 * 583 * Calling <code>registerSection</code> with a duplicate id will override previous registration. 584 * However, the request must come from the same calling uid. 585 * 586 * @param id the ID of the extended section. It should be unique system-wide, and be 587 * different from IDs of all existing section in 588 * frameworks/base/core/proto/android/os/incident.proto. 589 * Also see incident.proto for other rules about the ID. 590 * @param name the name to display in logs and/or stderr when taking an incident report 591 * containing this section, mainly for debugging purpose 592 * @param executor the executor used to run the callback 593 * @param callback the callback function to be invoked when an incident report with all sections 594 * or sections matching the given id is being taken 595 */ registerSection(int id, @NonNull String name, @NonNull @CallbackExecutor Executor executor, @NonNull DumpCallback callback)596 public void registerSection(int id, @NonNull String name, 597 @NonNull @CallbackExecutor Executor executor, @NonNull DumpCallback callback) { 598 Objects.requireNonNull(executor, "executor cannot be null"); 599 Objects.requireNonNull(callback, "callback cannot be null"); 600 try { 601 if (callback.mExecutor != null) { 602 throw new RuntimeException("Do not reuse DumpCallback objects when calling" 603 + " registerSection"); 604 } 605 callback.mExecutor = executor; 606 callback.mId = id; 607 final IIncidentManager service = getIIncidentManagerLocked(); 608 if (service == null) { 609 Slog.e(TAG, "registerSection can't find incident binder service"); 610 return; 611 } 612 service.registerSection(id, name, callback.mBinder); 613 } catch (RemoteException ex) { 614 Slog.e(TAG, "registerSection failed", ex); 615 } 616 } 617 618 /** 619 * Unregister an extended section dump function. The section must be previously registered with 620 * {@link #registerSection(int, String, DumpCallback)} by the same calling uid. 621 */ unregisterSection(int id)622 public void unregisterSection(int id) { 623 try { 624 final IIncidentManager service = getIIncidentManagerLocked(); 625 if (service == null) { 626 Slog.e(TAG, "unregisterSection can't find incident binder service"); 627 return; 628 } 629 service.unregisterSection(id); 630 } catch (RemoteException ex) { 631 Slog.e(TAG, "unregisterSection failed", ex); 632 } 633 } 634 635 /** 636 * Get the incident reports that are available for upload for the supplied 637 * broadcast recevier. 638 * 639 * @param receiverClass Class name of broadcast receiver in this package that 640 * was registered to retrieve reports. 641 * 642 * @return A list of {@link Uri Uris} that are awaiting upload. 643 */ 644 @RequiresPermission(allOf = { 645 android.Manifest.permission.DUMP, 646 android.Manifest.permission.PACKAGE_USAGE_STATS 647 }) getIncidentReportList(String receiverClass)648 public @NonNull List<Uri> getIncidentReportList(String receiverClass) { 649 List<String> strings; 650 try { 651 strings = getCompanionServiceLocked().getIncidentReportList( 652 mContext.getPackageName(), receiverClass); 653 } catch (RemoteException ex) { 654 throw new RuntimeException("System server or incidentd going down", ex); 655 } 656 final int size = strings.size(); 657 ArrayList<Uri> result = new ArrayList(size); 658 for (int i = 0; i < size; i++) { 659 result.add(Uri.parse(strings.get(i))); 660 } 661 return result; 662 } 663 664 /** 665 * Get the incident report with the given URI id. 666 * 667 * @param uri Identifier of the incident report. 668 * 669 * @return an IncidentReport object, or null if the incident report has been 670 * expired from disk. 671 */ 672 @RequiresPermission(allOf = { 673 android.Manifest.permission.DUMP, 674 android.Manifest.permission.PACKAGE_USAGE_STATS 675 }) getIncidentReport(Uri uri)676 public @Nullable IncidentReport getIncidentReport(Uri uri) { 677 final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID); 678 if (id == null) { 679 // If there's no report id, it's a bug report, so we can't return the incident 680 // report. 681 return null; 682 } 683 684 final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE); 685 if (pkg == null) { 686 throw new RuntimeException("Invalid URI: No " 687 + URI_PARAM_CALLING_PACKAGE + " parameter. " + uri); 688 } 689 690 final String cls = uri.getQueryParameter(URI_PARAM_RECEIVER_CLASS); 691 if (cls == null) { 692 throw new RuntimeException("Invalid URI: No " 693 + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri); 694 } 695 696 try { 697 return getCompanionServiceLocked().getIncidentReport(pkg, cls, id); 698 } catch (RemoteException ex) { 699 throw new RuntimeException("System server or incidentd going down", ex); 700 } 701 } 702 703 /** 704 * Delete the incident report with the given URI id. 705 * 706 * @param uri Identifier of the incident report. Pass null to delete all 707 * incident reports owned by this application. 708 */ 709 @RequiresPermission(allOf = { 710 android.Manifest.permission.DUMP, 711 android.Manifest.permission.PACKAGE_USAGE_STATS 712 }) deleteIncidentReports(Uri uri)713 public void deleteIncidentReports(Uri uri) { 714 if (uri == null) { 715 try { 716 getCompanionServiceLocked().deleteAllIncidentReports(mContext.getPackageName()); 717 } catch (RemoteException ex) { 718 throw new RuntimeException("System server or incidentd going down", ex); 719 } 720 } else { 721 final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE); 722 if (pkg == null) { 723 throw new RuntimeException("Invalid URI: No " 724 + URI_PARAM_CALLING_PACKAGE + " parameter. " + uri); 725 } 726 727 final String cls = uri.getQueryParameter(URI_PARAM_RECEIVER_CLASS); 728 if (cls == null) { 729 throw new RuntimeException("Invalid URI: No " 730 + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri); 731 } 732 733 final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID); 734 if (id == null) { 735 throw new RuntimeException("Invalid URI: No " 736 + URI_PARAM_REPORT_ID + " parameter. " + uri); 737 } 738 739 try { 740 getCompanionServiceLocked().deleteIncidentReports(pkg, cls, id); 741 } catch (RemoteException ex) { 742 throw new RuntimeException("System server or incidentd going down", ex); 743 } 744 } 745 } 746 reportIncidentInternal(IncidentReportArgs args)747 private void reportIncidentInternal(IncidentReportArgs args) { 748 try { 749 final IIncidentManager service = getIIncidentManagerLocked(); 750 if (service == null) { 751 Slog.e(TAG, "reportIncident can't find incident binder service"); 752 return; 753 } 754 service.reportIncident(args); 755 } catch (RemoteException ex) { 756 Slog.e(TAG, "reportIncident failed", ex); 757 } 758 } 759 getIIncidentManagerLocked()760 private IIncidentManager getIIncidentManagerLocked() throws RemoteException { 761 if (mIncidentService != null) { 762 return mIncidentService; 763 } 764 765 synchronized (mLock) { 766 if (mIncidentService != null) { 767 return mIncidentService; 768 } 769 mIncidentService = IIncidentManager.Stub.asInterface( 770 ServiceManager.getService(Context.INCIDENT_SERVICE)); 771 if (mIncidentService != null) { 772 mIncidentService.asBinder().linkToDeath(() -> { 773 synchronized (mLock) { 774 mIncidentService = null; 775 } 776 }, 0); 777 } 778 return mIncidentService; 779 } 780 } 781 getCompanionServiceLocked()782 private IIncidentCompanion getCompanionServiceLocked() throws RemoteException { 783 if (mCompanionService != null) { 784 return mCompanionService; 785 } 786 787 synchronized (this) { 788 if (mCompanionService != null) { 789 return mCompanionService; 790 } 791 mCompanionService = IIncidentCompanion.Stub.asInterface( 792 ServiceManager.getService(Context.INCIDENT_COMPANION_SERVICE)); 793 if (mCompanionService != null) { 794 mCompanionService.asBinder().linkToDeath(() -> { 795 synchronized (mLock) { 796 mCompanionService = null; 797 } 798 }, 0); 799 } 800 return mCompanionService; 801 } 802 } 803 } 804 805