1 /*
2  * Copyright (C) 2023 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 com.android.server.am;
18 
19 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_AUDIO;
20 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_BLUETOOTH;
21 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_CAMERA;
22 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_CDM;
23 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_LOCATION;
24 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK;
25 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_MICROPHONE;
26 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_PHONE_CALL;
27 import static android.app.ActivityManager.FOREGROUND_SERVICE_API_TYPE_USB;
28 import static android.os.Process.INVALID_UID;
29 
30 import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA;
31 
32 import android.annotation.IntDef;
33 import android.app.ActivityManager;
34 import android.app.ActivityManager.ForegroundServiceApiType;
35 import android.app.ForegroundServiceDelegationOptions;
36 import android.content.ComponentName;
37 import android.content.pm.ServiceInfo;
38 import android.os.Trace;
39 import android.util.ArrayMap;
40 import android.util.IntArray;
41 import android.util.LongArray;
42 import android.util.Slog;
43 import android.util.SparseArray;
44 import android.util.SparseIntArray;
45 
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.internal.util.FrameworkStatsLog;
48 
49 import java.lang.annotation.Retention;
50 import java.lang.annotation.RetentionPolicy;
51 import java.util.ArrayList;
52 
53 /**
54  * Responsible for handling logging of API events
55  * and associating APIs with currently running FGS.
56  * Also tracks FGS that are currently running.
57  */
58 public class ForegroundServiceTypeLoggerModule {
59 
60     private static final String TAG = "ForegroundServiceTypeLoggerModule";
61 
62     public static final int FGS_STATE_CHANGED_API_CALL = 4;
63 
64     public static final int FGS_API_BEGIN_WITH_FGS = 1;
65     public static final int FGS_API_END_WITH_FGS = 2;
66     public static final int FGS_API_END_WITHOUT_FGS = 3;
67     public static final int FGS_API_PAUSE = 4;
68     public static final int FGS_API_RESUME = 5;
69 
70     @IntDef(flag = false, prefix = { "FGS_API_" }, value = {
71             FGS_API_BEGIN_WITH_FGS,
72             FGS_API_END_WITH_FGS,
73             FGS_API_END_WITHOUT_FGS,
74             FGS_API_PAUSE,
75             FGS_API_RESUME,
76     })
77     @Retention(RetentionPolicy.SOURCE)
78     public @interface FgsApiState {}
79 
80 
81     private static class UidState {
82         // A stack that keeps a list of API calls by type.
83         // This represents the ongoing open APIs
84         // that are running on the system for each
85         // app in the system. They are keyed
86         // by the API type (represented as a number).
87         final SparseArray<FgsApiRecord> mApiOpenCalls = new SparseArray<>();
88 
89         // This stack represents the last close call made per type
90         // We only care about the last call made so we track the last close
91         // call made per type. This way, once the FGS closes
92         // we simply log the last API call made.
93         final SparseArray<FgsApiRecord> mApiClosedCalls = new SparseArray<>();
94 
95         // Here we track how many APIs are opened before any FGS is running.
96         // These counts will only be added to the open call count below if
97         // an FGS is started. If an FGS is NOT started, then this count should
98         // gradually hit zero as close calls are decremented.
99         final SparseIntArray mOpenedWithoutFgsCount = new SparseIntArray();
100 
101         // Here we keep track of the count of in-flight calls.
102         // We only want to log the first open call and the last
103         // close call so that we get the largest duration
104         // possible.
105         final SparseIntArray mOpenWithFgsCount = new SparseIntArray();
106 
107         // A stack that keeps a list of API calls in the order
108         // that they were called. This represents the ongoing
109         // open APIs that are running on the system for each
110         // app in the system. They are keyed by FGS Type
111         // to another ordered map, keyed by the component name
112         // to facilitate removing the record from the structure
113         final SparseArray<ArrayMap<ComponentName, ServiceRecord>> mRunningFgs = new SparseArray<>();
114 
115         // A map of API types to last FGS stop call timestamps
116         // We use this to get the duration an API was active after
117         // the stop call.
118         final SparseArray<Long> mLastFgsTimeStamp = new SparseArray<>();
119 
120         // A map of API types to first FGS start call timestamps
121         // We use this to get the duration an API was active after
122         // the stop call.
123         final SparseArray<Long> mFirstFgsTimeStamp = new SparseArray<>();
124     }
125 
126     // SparseArray that tracks all UIDs that have made various
127     // API calls. Keyed by UID.
128     private final SparseArray<UidState> mUids = new SparseArray<>();
129 
ForegroundServiceTypeLoggerModule()130     public ForegroundServiceTypeLoggerModule() {
131     }
132 
133     /**
134      * Used to log the start of a Foreground Service. The first API
135      * call of the right type will also be associated and logged
136      */
logForegroundServiceStart(int uid, int pid, ServiceRecord record)137     public void logForegroundServiceStart(int uid, int pid, ServiceRecord record) {
138         if (record.getComponentName() != null) {
139             final String traceTag = record.getComponentName().flattenToString() + ":" + uid;
140             Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, traceTag,
141                     "foregroundService", record.foregroundServiceType);
142         }
143         // initialize the UID stack
144         UidState uidState = mUids.get(uid);
145         if (uidState == null) {
146             uidState = new UidState();
147             mUids.put(uid, uidState);
148         }
149         // grab the appropriate types
150         final IntArray apiTypes =
151                 convertFgsTypeToApiTypes(record.foregroundServiceType);
152         if (apiTypes.size() == 0) {
153             Slog.w(TAG, "Foreground service start for UID: "
154                     + uid + " does not have any types");
155         }
156         // now we need to iterate through the types
157         // and insert the new record as needed
158         final IntArray apiTypesFound = new IntArray();
159         final LongArray timestampsFound = new LongArray();
160         for (int i = 0, size = apiTypes.size(); i < size; i++) {
161             final int apiType = apiTypes.get(i);
162             int fgsIndex = uidState.mRunningFgs.indexOfKey(apiType);
163             if (fgsIndex < 0) {
164                 uidState.mRunningFgs.put(apiType, new ArrayMap<>());
165                 fgsIndex = uidState.mRunningFgs.indexOfKey(apiType);
166                 uidState.mFirstFgsTimeStamp.put(apiType, System.currentTimeMillis());
167             }
168             final ArrayMap<ComponentName, ServiceRecord> fgsList =
169                     uidState.mRunningFgs.valueAt(fgsIndex);
170             fgsList.put(record.getComponentName(), record);
171             // now we want to figure out if this FGS is associated with any currently open API
172 
173             // retrieve the last API call for the type if there is one
174             if (uidState.mApiOpenCalls.contains(apiType)) {
175                 // update the open call count associated with an FGS
176                 // we want to dump the previously opened call count into the
177                 // opened with an FGS call count
178                 // then zero out the old count
179                 uidState.mOpenWithFgsCount
180                         .put(apiType, uidState.mOpenedWithoutFgsCount.get(apiType));
181                 uidState.mOpenedWithoutFgsCount.put(apiType, 0);
182                 apiTypesFound.add(apiType);
183                 final FgsApiRecord call = uidState.mApiOpenCalls.get(apiType);
184                 timestampsFound.add(call.mTimeStart);
185                 // associate the call
186                 call.mIsAssociatedWithFgs = true;
187                 call.mAssociatedFgsRecord = record;
188 
189                 // remove the APIs, since we've logged the API starts
190                 // so we don't need to log them again
191                 uidState.mApiOpenCalls.remove(apiType);
192             }
193         }
194         if (apiTypesFound.size() != 0) {
195             // log a state change
196             for (int i = 0, size = apiTypesFound.size(); i < size; i++) {
197                 logFgsApiEvent(record,
198                         FGS_STATE_CHANGED_API_CALL,
199                         FGS_API_BEGIN_WITH_FGS,
200                         apiTypesFound.get(i),
201                         timestampsFound.get(i));
202             }
203         }
204     }
205 
206     /**
207      * Logs when an FGS stops. The last associated closed API event
208      * will also be logged
209      */
logForegroundServiceStop(int uid, ServiceRecord record)210     public void logForegroundServiceStop(int uid, ServiceRecord record) {
211         // we need to log all the API end events and remove the start events
212         // then we remove the FGS from the various stacks
213         // and also clean up the start calls stack by UID
214         if (record.getComponentName() != null) {
215             final String traceTag = record.getComponentName().flattenToString() + ":" + uid;
216             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
217                     traceTag, record.hashCode());
218         }
219         final IntArray apiTypes = convertFgsTypeToApiTypes(record.foregroundServiceType);
220         final UidState uidState = mUids.get(uid);
221         if (apiTypes.size() == 0) {
222             Slog.w(TAG, "FGS stop call for: " + uid + " has no types!");
223         }
224         if (uidState == null) {
225             Slog.w(TAG, "FGS stop call being logged with no start call for UID for UID "
226                     + uid
227                     + " in package " + record.packageName);
228             return;
229         }
230         final ArrayList<Integer> apisFound = new ArrayList<>();
231         final ArrayList<Long> timestampsFound = new ArrayList<>();
232         for (int i = 0, size = apiTypes.size(); i < size; i++) {
233             final int apiType = apiTypes.get(i);
234 
235             // remove the FGS record from the stack
236             final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType =
237                     uidState.mRunningFgs.get(apiType);
238             if (runningFgsOfType == null) {
239                 Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid
240                         + " in package " + record.packageName);
241                 continue;
242             }
243 
244             runningFgsOfType.remove(record.getComponentName());
245             if (runningFgsOfType.size() == 0) {
246                 // there's no more FGS running for this type, just get rid of it
247                 uidState.mRunningFgs.remove(apiType);
248                 // but we need to keep track of the timestamp in case an API stops
249                 uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis());
250             }
251 
252             final int apiTypeIndex = uidState.mOpenWithFgsCount.indexOfKey(apiType);
253             if (apiTypeIndex < 0) {
254                 Slog.w(TAG, "Logger should be tracking FGS types correctly for UID " + uid
255                         + " in package " + record.packageName);
256                 continue;
257             }
258             // retrieve the eligible closed call
259             // we only want to log if this is the only
260             // open in flight call. If there are other calls,
261             // we just skip logging
262             final FgsApiRecord closedApi = uidState.mApiClosedCalls.get(apiType);
263             if (closedApi != null
264                     && uidState.mOpenWithFgsCount.valueAt(apiTypeIndex) == 0) {
265                 apisFound.add(apiType);
266                 timestampsFound.add(closedApi.mTimeStart);
267                 // remove the last API close call
268                 uidState.mApiClosedCalls.remove(apiType);
269             }
270         }
271         if (!apisFound.isEmpty()) {
272             // time to log the call
273             for (int i = 0; i < apisFound.size(); i++) {
274                 logFgsApiEvent(record,
275                         FGS_STATE_CHANGED_API_CALL,
276                         FGS_API_END_WITH_FGS, apisFound.get(i), timestampsFound.get(i));
277             }
278         }
279     }
280 
281     /**
282      * Called to log an API start call. If any associated FGS
283      * is running and this is the first open API call, then
284      * the event is logged.
285      */
logForegroundServiceApiEventBegin(@oregroundServiceApiType int apiType, int uid, int pid, String packageName)286     public long logForegroundServiceApiEventBegin(@ForegroundServiceApiType int apiType,
287             int uid, int pid, String packageName) {
288         final FgsApiRecord callStart =
289                 new FgsApiRecord(uid, pid, packageName, apiType, System.currentTimeMillis());
290         UidState uidState = mUids.get(uid);
291         if (uidState == null) {
292             uidState = new UidState();
293             mUids.put(uid, uidState);
294         }
295         // now we want to figure out if this call is associated with any FGS
296         // is there an FGS?
297         if (!hasValidActiveFgs(uid, apiType)) {
298             // no FGS running currently, so this API
299             // started without an FGS
300             // initialize the started without FGS count if it isn't already
301             int openWithoutFgsCountIndex =
302                     uidState.mOpenedWithoutFgsCount.indexOfKey(apiType);
303 
304             if (openWithoutFgsCountIndex < 0) {
305                 uidState.mOpenedWithoutFgsCount.put(apiType, 0);
306                 openWithoutFgsCountIndex =
307                         uidState.mOpenedWithoutFgsCount.indexOfKey(apiType);
308             }
309             // insert this record as the first open API call
310             // IF we do not have one
311             if (!uidState.mApiOpenCalls.contains(apiType)
312                     || uidState.mOpenedWithoutFgsCount.valueAt(openWithoutFgsCountIndex) == 0) {
313                 uidState.mApiOpenCalls.put(apiType, callStart);
314             }
315             // now update the count of the open API calls
316             // started without an FGS
317             uidState.mOpenedWithoutFgsCount
318                     .put(apiType, uidState.mOpenedWithoutFgsCount.get(apiType) + 1);
319             return callStart.mTimeStart;
320         }
321         // so there is an FGS running
322         // that we can associate with
323         // we now need to update the count
324         // for open calls that started
325         // with an FGS
326         int openWithFgsIndex = uidState.mOpenWithFgsCount.indexOfKey(apiType);
327 
328         if (openWithFgsIndex < 0) {
329             uidState.mOpenWithFgsCount.put(apiType, 0);
330             openWithFgsIndex = uidState.mOpenWithFgsCount.indexOfKey(apiType);
331         }
332         uidState.mOpenWithFgsCount
333                 .put(apiType, uidState.mOpenWithFgsCount.valueAt(openWithFgsIndex) + 1);
334         final ArrayMap<ComponentName, ServiceRecord> fgsListMap = uidState.mRunningFgs.get(apiType);
335 
336         // now we get the relevant FGS to log with
337         final int apiTypes = apiType;
338         final long timestamps = callStart.mTimeStart;
339         if (uidState.mOpenWithFgsCount.valueAt(openWithFgsIndex) == 1) {
340             for (ServiceRecord record : fgsListMap.values()) {
341                 logFgsApiEvent(record,
342                         FGS_STATE_CHANGED_API_CALL,
343                         FGS_API_BEGIN_WITH_FGS,
344                         apiTypes,
345                         timestamps);
346             }
347         }
348         return callStart.mTimeStart;
349     }
350 
351     /**
352      * Called to log the end of an API call. If this
353      * is the last API close call, it will be logged
354      * as an event.
355      */
logForegroundServiceApiEventEnd(@oregroundServiceApiType int apiType, int uid, int pid)356     public long logForegroundServiceApiEventEnd(@ForegroundServiceApiType int apiType,
357             int uid, int pid) {
358         // are there even FGS that we want to associate with?
359         // if there's even an entry in the open call count,
360         // then we should care, otherwise we assume
361         // it's not related to any FGS
362         UidState uidState = mUids.get(uid);
363         if (uidState == null) {
364             Slog.w(TAG, "API event end called before start!");
365             return -1;
366         }
367         final int apiIndex = uidState.mOpenWithFgsCount.indexOfKey(apiType);
368         if (apiIndex >= 0) {
369             // are there any calls that started with an FGS?
370             if (uidState.mOpenWithFgsCount.get(apiType) != 0) {
371                 // we should decrement the count, since we only
372                 // want to log the last close call
373                 uidState.mOpenWithFgsCount.put(apiType,
374                         uidState.mOpenWithFgsCount.get(apiType) - 1);
375             }
376             // is there no FGS running and this is the last close call?
377             if (!hasValidActiveFgs(uid, apiType)
378                     && uidState.mOpenWithFgsCount.get(apiType) == 0) {
379                 // we just log that an event happened w/ no
380                 // FGS associated. This is to avoid dangling
381                 // events
382                 final long timestamp = System.currentTimeMillis();
383                 final int apiTypes = apiType;
384                 logFgsApiEventWithNoFgs(uid, FGS_API_END_WITHOUT_FGS, apiTypes, timestamp);
385                 // we should now remove the count, so as to signal that
386                 // there was never an FGS called that can be associated
387                 uidState.mOpenWithFgsCount.removeAt(apiIndex);
388                 return timestamp;
389             }
390         }
391         // we know now that this call is not coming from an
392         // open FGS associated API call. So it is likely
393         // a part of an unassociated call that has now been
394         // closed. So we decrement that count
395         if (uidState.mOpenedWithoutFgsCount.indexOfKey(apiType) < 0) {
396             // initialize if we don't contain
397             uidState.mOpenedWithoutFgsCount.put(apiType, 0);
398         }
399         int apiOpenWithoutFgsCount = uidState.mOpenedWithoutFgsCount.get(apiType);
400         if (apiOpenWithoutFgsCount != 0) {
401             apiOpenWithoutFgsCount -= 1;
402             if (apiOpenWithoutFgsCount == 0) {
403                 uidState.mApiOpenCalls.remove(apiType);
404             }
405             uidState.mOpenedWithoutFgsCount
406                     .put(apiType, apiOpenWithoutFgsCount);
407             return System.currentTimeMillis();
408         }
409         // This is a part of a valid active FGS
410         // we should definitely update the pointer to the
411         // last closed API call
412         final SparseArray<FgsApiRecord> callsByUid = uidState.mApiClosedCalls;
413         final FgsApiRecord closedCall =
414                 new FgsApiRecord(uid, pid, "", apiType, System.currentTimeMillis());
415         uidState.mApiClosedCalls.put(apiType, closedCall);
416         return closedCall.mTimeStart;
417     }
418 
419     /**
420      * Log an API state change. This is only to be used by media playback
421      */
logForegroundServiceApiStateChanged(@oregroundServiceApiType int apiType, int uid, int pid, int state)422     public void logForegroundServiceApiStateChanged(@ForegroundServiceApiType int apiType,
423             int uid, int pid, int state) {
424         UidState uidState = mUids.get(uid);
425         if (!uidState.mRunningFgs.contains(apiType)) {
426             // if there is no FGS running for this type that could mean that
427             // either the FGS was stopped a while ago or
428             // that the API call was never run with an FGS
429             return;
430         }
431         final ArrayMap<ComponentName, ServiceRecord> fgsRecords = uidState.mRunningFgs.get(apiType);
432         final int apiTypes = apiType;
433         final long timestamp = System.currentTimeMillis();
434         for (ServiceRecord record : fgsRecords.values()) {
435             logFgsApiEvent(record,
436                     FGS_STATE_CHANGED_API_CALL,
437                     state,
438                     apiTypes,
439                     timestamp);
440         }
441     }
442 
convertFgsTypeToApiTypes(int fgsType)443     private IntArray convertFgsTypeToApiTypes(int fgsType) {
444         final IntArray types = new IntArray();
445         if ((fgsType & ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA)
446                 == ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA) {
447             types.add(FOREGROUND_SERVICE_API_TYPE_CAMERA);
448         }
449         if ((fgsType & ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
450                 == ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE) {
451             types.add(FOREGROUND_SERVICE_API_TYPE_BLUETOOTH);
452             types.add(FOREGROUND_SERVICE_API_TYPE_USB);
453             types.add(FOREGROUND_SERVICE_API_TYPE_CDM);
454         }
455         if ((fgsType & ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION)
456                 == ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION) {
457             types.add(FOREGROUND_SERVICE_API_TYPE_LOCATION);
458         }
459         if ((fgsType & ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
460                 == ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) {
461             types.add(FOREGROUND_SERVICE_API_TYPE_AUDIO);
462             types.add(FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK);
463         }
464         if ((fgsType & ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE)
465                 == ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE) {
466             types.add(FOREGROUND_SERVICE_API_TYPE_MICROPHONE);
467         }
468         if ((fgsType & ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL)
469                 == ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL) {
470             types.add(FOREGROUND_SERVICE_API_TYPE_PHONE_CALL);
471         }
472         return types;
473     }
474 
hasValidActiveFgs(int uid, @ForegroundServiceApiType int apiType)475     private boolean hasValidActiveFgs(int uid, @ForegroundServiceApiType int apiType) {
476         UidState uidState = mUids.get(uid);
477         if (uidState != null) {
478             return uidState.mRunningFgs.contains(apiType);
479         }
480         return false;
481     }
482 
483     /**
484      * Logs an API event that occurred while an FGS was running
485      */
486     @VisibleForTesting
logFgsApiEvent(ServiceRecord r, int fgsState, @FgsApiState int apiState, @ForegroundServiceApiType int apiType, long timestamp)487     public void logFgsApiEvent(ServiceRecord r, int fgsState,
488             @FgsApiState int apiState,
489             @ForegroundServiceApiType int apiType, long timestamp) {
490         long apiDurationBeforeFgsStart = 0;
491         long apiDurationAfterFgsEnd = 0;
492         UidState uidState = mUids.get(r.appInfo.uid);
493         if (uidState == null) {
494             return;
495         }
496         if (uidState.mFirstFgsTimeStamp.contains(apiType)) {
497             apiDurationBeforeFgsStart = uidState.mFirstFgsTimeStamp.get(apiType) - timestamp;
498         }
499         if (uidState.mLastFgsTimeStamp.contains(apiType)) {
500             apiDurationAfterFgsEnd = timestamp - uidState.mLastFgsTimeStamp.get(apiType);
501         }
502         final int[] apiTypes = new int[1];
503         apiTypes[0] = apiType;
504         final long[] timeStamps = new long[1];
505         timeStamps[0] = timestamp;
506         FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
507                 r.appInfo.uid,
508                 r.shortInstanceName,
509                 fgsState, // FGS State
510                 // TODO: Also log "forStart"
511                 r.isFgsAllowedWiu_forCapabilities(), // allowWhileInUsePermissionInFgs
512                 r.getFgsAllowStart(), // fgsStartReasonCode
513                 r.appInfo.targetSdkVersion,
514                 r.mRecentCallingUid,
515                 0, // callerTargetSdkVersion
516                 r.mInfoTempFgsAllowListReason != null
517                         ? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
518                 r.mFgsNotificationWasDeferred,
519                 r.mFgsNotificationShown,
520                 0, // durationMs
521                 r.mStartForegroundCount,
522                 0, // Short instance name -- no longer logging it.
523                 r.mFgsHasNotificationPermission,
524                 r.foregroundServiceType,
525                 0,
526                 r.mIsFgsDelegate,
527                 r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID,
528                 r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService
529                         : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT,
530                 apiState,
531                 apiTypes,
532                 timeStamps,
533                 ActivityManager.PROCESS_STATE_UNKNOWN,
534                 ActivityManager.PROCESS_CAPABILITY_NONE,
535                 ActivityManager.PROCESS_STATE_UNKNOWN,
536                 ActivityManager.PROCESS_CAPABILITY_NONE,
537                 apiDurationBeforeFgsStart,
538                 apiDurationAfterFgsEnd,
539                 r.mAllowWiu_noBinding,
540                 r.mAllowWiu_inBindService,
541                 r.mAllowWiu_byBindings,
542                 r.mAllowStart_noBinding,
543                 r.mAllowStart_inBindService,
544                 r.mAllowStart_byBindings,
545                 FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
546                 false
547         );
548     }
549 
550     /**
551      * Logs an API event that occurred while no FGS was running.
552      * Only used to log API exit events
553      */
554     @VisibleForTesting
logFgsApiEventWithNoFgs(int uid, @FgsApiState int apiState, @ForegroundServiceApiType int apiType, long timestamp)555     public void logFgsApiEventWithNoFgs(int uid,
556             @FgsApiState int apiState,
557             @ForegroundServiceApiType int apiType, long timestamp) {
558         long apiDurationAfterFgsEnd = 0;
559         UidState uidState = mUids.get(uid);
560         if (uidState == null) {
561             return;
562         }
563         if (uidState.mLastFgsTimeStamp.contains(apiType)) {
564             apiDurationAfterFgsEnd = timestamp - uidState.mLastFgsTimeStamp.get(apiType);
565         }
566         final int[] apiTypes = new int[1];
567         apiTypes[0] = apiType;
568         final long[] timeStamps = new long[1];
569         timeStamps[0] = timestamp;
570         FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
571                 uid,
572                 null,
573                 FGS_STATE_CHANGED_API_CALL,
574                 false, // allowWhileInUsePermissionInFgs
575                 0, // fgsStartReasonCode
576                 0,
577                 uid,
578                 0, // callerTargetSdkVersion
579                 0,
580                 false,
581                 false,
582                 0, // durationMs
583                 0,
584                 0,
585                 false,
586                 0,
587                 0,
588                 false,
589                 0,
590                 0,
591                 apiState,
592                 apiTypes,
593                 timeStamps,
594                 ActivityManager.PROCESS_STATE_UNKNOWN,
595                 ActivityManager.PROCESS_CAPABILITY_NONE,
596                 ActivityManager.PROCESS_STATE_UNKNOWN,
597                 ActivityManager.PROCESS_CAPABILITY_NONE,
598                 0,
599                 apiDurationAfterFgsEnd,
600                 0,
601                 0,
602                 0,
603                 0,
604                 0,
605                 0,
606                 FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
607                 false
608         );
609     }
610 
611     /**
612      * Internal class for tracking open API calls
613      */
614     private static class FgsApiRecord {
615         final int mUid; // the UID from where the API call came from
616         final int mPid; // the PID from where the API call came from
617         final String mPackageName; // the package name from where the API call came from
618         @ForegroundServiceApiType
619         int mType; // the type of API call (camera, etc)
620         boolean mIsAssociatedWithFgs; // is it associated with an FGS?
621         ServiceRecord mAssociatedFgsRecord; // the FGS it is associated with
622         final long mTimeStart; // timestamp for the event
623 
FgsApiRecord(int uid, int pid, String packageName, @ForegroundServiceApiType int type, long timeStart)624         FgsApiRecord(int uid,
625                 int pid,
626                 String packageName,
627                 @ForegroundServiceApiType int type,
628                 long timeStart) {
629             this.mUid = uid;
630             this.mPid = pid;
631             this.mPackageName = packageName;
632             this.mType = type;
633             this.mTimeStart = timeStart;
634         }
635     }
636 }
637