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