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