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