• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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