• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2025 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.media;
18 
19 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_SCANNING_STARTED;
20 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_SCANNING_STOPPED;
21 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_FAILED_TO_REROUTE_SYSTEM_MEDIA;
22 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_INVALID_COMMAND;
23 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_NETWORK_ERROR;
24 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_REJECTED;
25 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_ROUTE_NOT_AVAILABLE;
26 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNIMPLEMENTED;
27 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNKNOWN_ERROR;
28 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNSPECIFIED;
29 
30 import android.annotation.NonNull;
31 import android.media.MediaRoute2ProviderService;
32 import android.util.Log;
33 import android.util.Slog;
34 import com.android.internal.annotations.VisibleForTesting;
35 import java.io.PrintWriter;
36 import java.util.LinkedHashMap;
37 import java.util.Map;
38 
39 /**
40  * Logs metrics for MediaRouter2.
41  *
42  * @hide
43  */
44 final class MediaRouterMetricLogger {
45     private static final String TAG = "MediaRouterMetricLogger";
46     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
47     private static final int REQUEST_INFO_CACHE_CAPACITY = 100;
48 
49     /** LRU cache to store request info. */
50     private final RequestInfoCache mRequestInfoCache;
51 
52     /** Constructor for {@link MediaRouterMetricLogger}. */
MediaRouterMetricLogger()53     public MediaRouterMetricLogger() {
54         mRequestInfoCache = new RequestInfoCache(REQUEST_INFO_CACHE_CAPACITY);
55     }
56 
57     /**
58      * Adds a new request info to the cache.
59      *
60      * @param uniqueRequestId The unique request id.
61      * @param eventType The event type.
62      */
addRequestInfo(long uniqueRequestId, int eventType)63     public void addRequestInfo(long uniqueRequestId, int eventType) {
64         RequestInfo requestInfo = new RequestInfo(uniqueRequestId, eventType);
65         mRequestInfoCache.put(requestInfo.mUniqueRequestId, requestInfo);
66     }
67 
68     /**
69      * Removes a request info from the cache.
70      *
71      * @param uniqueRequestId The unique request id.
72      */
removeRequestInfo(long uniqueRequestId)73     public void removeRequestInfo(long uniqueRequestId) {
74         mRequestInfoCache.remove(uniqueRequestId);
75     }
76 
77     /**
78      * Logs an operation failure.
79      *
80      * @param eventType The event type.
81      * @param result The result of the operation.
82      */
logOperationFailure(int eventType, int result)83     public void logOperationFailure(int eventType, int result) {
84         logMediaRouterEvent(eventType, result);
85     }
86 
87     /**
88      * Logs an operation triggered.
89      *
90      * @param eventType The event type.
91      */
logOperationTriggered(int eventType, int result)92     public void logOperationTriggered(int eventType, int result) {
93         logMediaRouterEvent(eventType, result);
94     }
95 
96     /**
97      * Logs the result of a request.
98      *
99      * @param uniqueRequestId The unique request id.
100      * @param result The result of the request.
101      */
logRequestResult(long uniqueRequestId, int result)102     public void logRequestResult(long uniqueRequestId, int result) {
103         RequestInfo requestInfo = mRequestInfoCache.get(uniqueRequestId);
104         if (requestInfo == null) {
105             Slog.w(
106                     TAG,
107                     "logRequestResult: No RequestInfo found for uniqueRequestId="
108                             + uniqueRequestId);
109             return;
110         }
111 
112         int eventType = requestInfo.mEventType;
113         logMediaRouterEvent(eventType, result);
114 
115         removeRequestInfo(uniqueRequestId);
116     }
117 
118     /**
119      * Logs the overall scanning state.
120      *
121      * @param isScanning The scanning state for the user.
122      */
updateScanningState(boolean isScanning)123     public void updateScanningState(boolean isScanning) {
124         if (!isScanning) {
125             logScanningStopped();
126         } else {
127             logScanningStarted();
128         }
129     }
130 
131     /** Logs the scanning started event. */
logScanningStarted()132     private void logScanningStarted() {
133         logMediaRouterEvent(
134                 MEDIA_ROUTER_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_SCANNING_STARTED,
135                 MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNSPECIFIED);
136     }
137 
138     /** Logs the scanning stopped event. */
logScanningStopped()139     private void logScanningStopped() {
140         logMediaRouterEvent(
141                 MEDIA_ROUTER_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_SCANNING_STOPPED,
142                 MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNSPECIFIED);
143     }
144 
145     /**
146      * Converts a reason code from {@link MediaRoute2ProviderService} to a result code for logging.
147      *
148      * @param reason The reason code from {@link MediaRoute2ProviderService}.
149      * @return The result code for logging.
150      */
convertResultFromReason(int reason)151     public static int convertResultFromReason(int reason) {
152         switch (reason) {
153             case MediaRoute2ProviderService.REASON_UNKNOWN_ERROR:
154                 return MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNKNOWN_ERROR;
155             case MediaRoute2ProviderService.REASON_REJECTED:
156                 return MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_REJECTED;
157             case MediaRoute2ProviderService.REASON_NETWORK_ERROR:
158                 return MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_NETWORK_ERROR;
159             case MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE:
160                 return MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_ROUTE_NOT_AVAILABLE;
161             case MediaRoute2ProviderService.REASON_INVALID_COMMAND:
162                 return MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_INVALID_COMMAND;
163             case MediaRoute2ProviderService.REASON_UNIMPLEMENTED:
164                 return MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNIMPLEMENTED;
165             case MediaRoute2ProviderService.REASON_FAILED_TO_REROUTE_SYSTEM_MEDIA:
166                 return MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_FAILED_TO_REROUTE_SYSTEM_MEDIA;
167             default:
168                 return MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNSPECIFIED;
169         }
170     }
171 
172     /**
173      * Gets the size of the request info cache.
174      *
175      * @return The size of the request info cache.
176      */
177     @VisibleForTesting
getRequestCacheSize()178     public int getRequestCacheSize() {
179         return mRequestInfoCache.size();
180     }
181 
logMediaRouterEvent(int eventType, int result)182     private void logMediaRouterEvent(int eventType, int result) {
183         MediaRouterStatsLog.write(
184                 MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED, eventType, result);
185 
186         if (DEBUG) {
187             Slog.d(TAG, "logMediaRouterEvent: " + eventType + " " + result);
188         }
189     }
190 
191     /** A cache for storing request info that evicts entries when it reaches its capacity. */
192     class RequestInfoCache extends LinkedHashMap<Long, RequestInfo> {
193 
194         public final int capacity;
195 
196         /**
197          * Constructor for {@link RequestInfoCache}.
198          *
199          * @param capacity The maximum capacity of the cache.
200          */
RequestInfoCache(int capacity)201         public RequestInfoCache(int capacity) {
202             super(capacity, 1.0f, true);
203             this.capacity = capacity;
204         }
205 
206         @Override
removeEldestEntry(Map.Entry<Long, RequestInfo> eldest)207         protected boolean removeEldestEntry(Map.Entry<Long, RequestInfo> eldest) {
208             boolean shouldRemove = size() > capacity;
209             if (shouldRemove) {
210                 Slog.d(TAG, "Evicted request info: " + eldest.getValue());
211                 logOperationTriggered(
212                         eldest.getValue().mEventType,
213                         MEDIA_ROUTER_EVENT_REPORTED__RESULT__RESULT_UNSPECIFIED);
214             }
215             return shouldRemove;
216         }
217     }
218 
219     /** Class to store request info. */
220     static class RequestInfo {
221         public final long mUniqueRequestId;
222         public final int mEventType;
223 
224         /**
225          * Constructor for {@link RequestInfo}.
226          *
227          * @param uniqueRequestId The unique request id.
228          * @param eventType The event type.
229          */
RequestInfo(long uniqueRequestId, int eventType)230         RequestInfo(long uniqueRequestId, int eventType) {
231             mUniqueRequestId = uniqueRequestId;
232             mEventType = eventType;
233         }
234 
235         /**
236          * Dumps the request info.
237          *
238          * @param pw The print writer.
239          * @param prefix The prefix for the output.
240          */
dump(@onNull PrintWriter pw, @NonNull String prefix)241         public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
242             pw.println(prefix + "RequestInfo");
243             String indent = prefix + "  ";
244             pw.println(indent + "mUniqueRequestId=" + mUniqueRequestId);
245             pw.println(indent + "mEventType=" + mEventType);
246         }
247     }
248 }
249