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