• 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.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