blob: 755438b1a4135174c94269c0045f02de2c7a6fa4 [file] [log] [blame]
Hongyi Zhang700137e2019-05-23 21:19:36 -07001/*
2 * Copyright (C) 2019 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
Soonil Nagarkar3f128402019-12-12 08:31:27 -080017package com.android.server.location;
18
19import static com.android.server.LocationManagerService.TAG;
Hongyi Zhang700137e2019-05-23 21:19:36 -070020
21import android.app.ActivityManager;
Hongyi Zhang0e9ea752019-06-09 22:49:25 -070022import android.location.Geofence;
Hongyi Zhang700137e2019-05-23 21:19:36 -070023import android.location.LocationManager;
24import android.location.LocationRequest;
Hongyi Zhang700137e2019-05-23 21:19:36 -070025import android.stats.location.LocationStatsEnums;
26import android.util.Log;
27import android.util.StatsLog;
28
Soonil Nagarkar3f128402019-12-12 08:31:27 -080029import com.android.internal.annotations.GuardedBy;
30
Hongyi Zhang700137e2019-05-23 21:19:36 -070031import java.time.Instant;
32
33/**
34 * Logger for Location API usage logging.
35 */
Sasha Kuznetsovb9f26b42019-10-03 17:30:46 -070036public class LocationUsageLogger {
Hongyi Zhang700137e2019-05-23 21:19:36 -070037
38 private static final int ONE_SEC_IN_MILLIS = 1000;
39 private static final int ONE_MINUTE_IN_MILLIS = 60000;
40 private static final int ONE_HOUR_IN_MILLIS = 3600000;
41
Hongyi Zhang700137e2019-05-23 21:19:36 -070042 private static final int API_USAGE_LOG_HOURLY_CAP = 60;
43
Soonil Nagarkar3f128402019-12-12 08:31:27 -080044 @GuardedBy("this")
45 private long mLastApiUsageLogHour = 0;
46 @GuardedBy("this")
47 private int mApiUsageLogHourlyCount = 0;
48
49 /**
50 * Log a location API usage event.
51 */
52 public void logLocationApiUsage(int usageType, int apiInUse,
53 String packageName, LocationRequest locationRequest,
54 boolean hasListener, boolean hasIntent,
55 Geofence geofence, int activityImportance) {
56 try {
57 if (hitApiUsageLogCap()) {
58 return;
59 }
60
61 boolean isLocationRequestNull = locationRequest == null;
62 boolean isGeofenceNull = geofence == null;
63
64 StatsLog.write(StatsLog.LOCATION_MANAGER_API_USAGE_REPORTED, usageType,
65 apiInUse, packageName,
66 isLocationRequestNull
67 ? LocationStatsEnums.PROVIDER_UNKNOWN
68 : bucketizeProvider(locationRequest.getProvider()),
69 isLocationRequestNull
70 ? LocationStatsEnums.QUALITY_UNKNOWN
71 : locationRequest.getQuality(),
72 isLocationRequestNull
73 ? LocationStatsEnums.INTERVAL_UNKNOWN
74 : bucketizeInterval(locationRequest.getInterval()),
75 isLocationRequestNull
76 ? LocationStatsEnums.DISTANCE_UNKNOWN
77 : bucketizeDistance(
78 locationRequest.getSmallestDisplacement()),
79 isLocationRequestNull ? 0 : locationRequest.getNumUpdates(),
80 // only log expireIn for USAGE_STARTED
81 isLocationRequestNull || usageType == LocationStatsEnums.USAGE_ENDED
82 ? LocationStatsEnums.EXPIRATION_UNKNOWN
83 : bucketizeExpireIn(locationRequest.getExpireIn()),
84 getCallbackType(apiInUse, hasListener, hasIntent),
85 isGeofenceNull
86 ? LocationStatsEnums.RADIUS_UNKNOWN
87 : bucketizeRadius(geofence.getRadius()),
88 categorizeActivityImportance(activityImportance));
89 } catch (Exception e) {
90 // Swallow exceptions to avoid crashing LMS.
91 Log.w(TAG, "Failed to log API usage to statsd.", e);
92 }
93 }
94
95 /**
96 * Log a location API usage event.
97 */
98 public void logLocationApiUsage(int usageType, int apiInUse, String providerName) {
99 try {
100 if (hitApiUsageLogCap()) {
101 return;
102 }
103
104 StatsLog.write(StatsLog.LOCATION_MANAGER_API_USAGE_REPORTED, usageType, apiInUse,
105 /* package_name= */ null,
106 bucketizeProvider(providerName),
107 LocationStatsEnums.QUALITY_UNKNOWN,
108 LocationStatsEnums.INTERVAL_UNKNOWN,
109 LocationStatsEnums.DISTANCE_UNKNOWN,
110 /* numUpdates= */ 0,
111 LocationStatsEnums.EXPIRATION_UNKNOWN,
112 getCallbackType(
113 apiInUse,
114 /* isListenerNull= */ true,
115 /* isIntentNull= */ true),
116 /* bucketizedRadius= */ 0,
117 LocationStatsEnums.IMPORTANCE_UNKNOWN);
118 } catch (Exception e) {
119 Log.w(TAG, "Failed to log API usage to statsd.", e);
120 }
121 }
122
123 private static int bucketizeProvider(String provider) {
Hongyi Zhang700137e2019-05-23 21:19:36 -0700124 if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
125 return LocationStatsEnums.PROVIDER_NETWORK;
126 } else if (LocationManager.GPS_PROVIDER.equals(provider)) {
127 return LocationStatsEnums.PROVIDER_GPS;
128 } else if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
129 return LocationStatsEnums.PROVIDER_PASSIVE;
130 } else if (LocationManager.FUSED_PROVIDER.equals(provider)) {
131 return LocationStatsEnums.PROVIDER_FUSED;
132 } else {
133 return LocationStatsEnums.PROVIDER_UNKNOWN;
134 }
135 }
136
Soonil Nagarkar3f128402019-12-12 08:31:27 -0800137 private static int bucketizeInterval(long interval) {
Hongyi Zhang700137e2019-05-23 21:19:36 -0700138 if (interval < ONE_SEC_IN_MILLIS) {
139 return LocationStatsEnums.INTERVAL_BETWEEN_0_SEC_AND_1_SEC;
140 } else if (interval < ONE_SEC_IN_MILLIS * 5) {
141 return LocationStatsEnums.INTERVAL_BETWEEN_1_SEC_AND_5_SEC;
142 } else if (interval < ONE_MINUTE_IN_MILLIS) {
143 return LocationStatsEnums.INTERVAL_BETWEEN_5_SEC_AND_1_MIN;
144 } else if (interval < ONE_MINUTE_IN_MILLIS * 10) {
145 return LocationStatsEnums.INTERVAL_BETWEEN_1_MIN_AND_10_MIN;
146 } else if (interval < ONE_HOUR_IN_MILLIS) {
147 return LocationStatsEnums.INTERVAL_BETWEEN_10_MIN_AND_1_HOUR;
148 } else {
149 return LocationStatsEnums.INTERVAL_LARGER_THAN_1_HOUR;
150 }
151 }
152
Soonil Nagarkar3f128402019-12-12 08:31:27 -0800153 private static int bucketizeDistance(float smallestDisplacement) {
154 if (smallestDisplacement <= 0) {
Hongyi Zhang700137e2019-05-23 21:19:36 -0700155 return LocationStatsEnums.DISTANCE_ZERO;
156 } else if (smallestDisplacement > 0 && smallestDisplacement <= 100) {
157 return LocationStatsEnums.DISTANCE_BETWEEN_0_AND_100;
158 } else {
159 return LocationStatsEnums.DISTANCE_LARGER_THAN_100;
160 }
161 }
162
Soonil Nagarkar3f128402019-12-12 08:31:27 -0800163 private static int bucketizeRadius(float radius) {
Hongyi Zhang700137e2019-05-23 21:19:36 -0700164 if (radius < 0) {
165 return LocationStatsEnums.RADIUS_NEGATIVE;
166 } else if (radius < 100) {
167 return LocationStatsEnums.RADIUS_BETWEEN_0_AND_100;
168 } else if (radius < 200) {
169 return LocationStatsEnums.RADIUS_BETWEEN_100_AND_200;
170 } else if (radius < 300) {
171 return LocationStatsEnums.RADIUS_BETWEEN_200_AND_300;
172 } else if (radius < 1000) {
173 return LocationStatsEnums.RADIUS_BETWEEN_300_AND_1000;
174 } else if (radius < 10000) {
175 return LocationStatsEnums.RADIUS_BETWEEN_1000_AND_10000;
176 } else {
177 return LocationStatsEnums.RADIUS_LARGER_THAN_100000;
178 }
179 }
180
Soonil Nagarkar3f128402019-12-12 08:31:27 -0800181 private static int bucketizeExpireIn(long expireIn) {
182 if (expireIn == Long.MAX_VALUE) {
Hongyi Zhang700137e2019-05-23 21:19:36 -0700183 return LocationStatsEnums.EXPIRATION_NO_EXPIRY;
184 }
185
Hongyi Zhang700137e2019-05-23 21:19:36 -0700186 if (expireIn < 20 * ONE_SEC_IN_MILLIS) {
187 return LocationStatsEnums.EXPIRATION_BETWEEN_0_AND_20_SEC;
188 } else if (expireIn < ONE_MINUTE_IN_MILLIS) {
189 return LocationStatsEnums.EXPIRATION_BETWEEN_20_SEC_AND_1_MIN;
190 } else if (expireIn < ONE_MINUTE_IN_MILLIS * 10) {
191 return LocationStatsEnums.EXPIRATION_BETWEEN_1_MIN_AND_10_MIN;
192 } else if (expireIn < ONE_HOUR_IN_MILLIS) {
193 return LocationStatsEnums.EXPIRATION_BETWEEN_10_MIN_AND_1_HOUR;
194 } else {
195 return LocationStatsEnums.EXPIRATION_LARGER_THAN_1_HOUR;
196 }
197 }
198
199 private static int categorizeActivityImportance(int importance) {
200 if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
201 return LocationStatsEnums.IMPORTANCE_TOP;
202 } else if (importance == ActivityManager
Soonil Nagarkar3f128402019-12-12 08:31:27 -0800203 .RunningAppProcessInfo
204 .IMPORTANCE_FOREGROUND_SERVICE) {
Hongyi Zhang700137e2019-05-23 21:19:36 -0700205 return LocationStatsEnums.IMPORTANCE_FORGROUND_SERVICE;
206 } else {
207 return LocationStatsEnums.IMPORTANCE_BACKGROUND;
208 }
209 }
210
211 private static int getCallbackType(
212 int apiType, boolean hasListener, boolean hasIntent) {
213 if (apiType == LocationStatsEnums.API_SEND_EXTRA_COMMAND) {
214 return LocationStatsEnums.CALLBACK_NOT_APPLICABLE;
215 }
216
217 // Listener and PendingIntent will not be set at
218 // the same time.
219 if (hasIntent) {
220 return LocationStatsEnums.CALLBACK_PENDING_INTENT;
221 } else if (hasListener) {
222 return LocationStatsEnums.CALLBACK_LISTENER;
223 } else {
224 return LocationStatsEnums.CALLBACK_UNKNOWN;
225 }
226 }
227
Soonil Nagarkar3f128402019-12-12 08:31:27 -0800228 private synchronized boolean hitApiUsageLogCap() {
Hongyi Zhang700137e2019-05-23 21:19:36 -0700229 long currentHour = Instant.now().toEpochMilli() / ONE_HOUR_IN_MILLIS;
230 if (currentHour > mLastApiUsageLogHour) {
231 mLastApiUsageLogHour = currentHour;
232 mApiUsageLogHourlyCount = 0;
Soonil Nagarkar3f128402019-12-12 08:31:27 -0800233 return false;
Hongyi Zhang700137e2019-05-23 21:19:36 -0700234 } else {
235 mApiUsageLogHourlyCount = Math.min(
Soonil Nagarkar3f128402019-12-12 08:31:27 -0800236 mApiUsageLogHourlyCount + 1, API_USAGE_LOG_HOURLY_CAP);
237 return mApiUsageLogHourlyCount >= API_USAGE_LOG_HOURLY_CAP;
Hongyi Zhang700137e2019-05-23 21:19:36 -0700238 }
239 }
240}