1 /* 2 * Copyright (C) 2017 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.google.android.mobly.snippet.event; 18 19 import com.google.android.mobly.snippet.util.Log; 20 import java.util.Deque; 21 import java.util.HashMap; 22 import java.util.Locale; 23 import java.util.Map; 24 import java.util.concurrent.LinkedBlockingDeque; 25 26 /** 27 * Manage the event queue. 28 * 29 * <p>EventCache APIs interact with the SnippetEvent cache - a data structure that holds {@link 30 * SnippetEvent} objects posted from snippet classes. The SnippetEvent cache provides a useful means 31 * of recording background events (such as sensor data) when the phone is busy with foreground 32 * activities. 33 */ 34 public class EventCache { 35 private static final String EVENT_DEQUE_ID_TEMPLATE = "%s|%s"; 36 private static final int EVENT_DEQUE_MAX_SIZE = 1024; 37 38 // A Map with each value being the queue for a particular type of event, and the key being the 39 // unique ID of the queue. The ID is composed of a callback ID and an event's name. 40 private final Map<String, LinkedBlockingDeque<SnippetEvent>> mEventDeques = new HashMap<>(); 41 42 private static volatile EventCache mEventCache; 43 EventCache()44 private EventCache() {} 45 getInstance()46 public static EventCache getInstance() { 47 if (mEventCache == null) { 48 synchronized (EventCache.class) { 49 if (mEventCache == null) { 50 mEventCache = new EventCache(); 51 } 52 } 53 } 54 return mEventCache; 55 } 56 getQueueId(String callbackId, String name)57 public static String getQueueId(String callbackId, String name) { 58 return String.format(Locale.US, EVENT_DEQUE_ID_TEMPLATE, callbackId, name); 59 } 60 getEventDeque(String qId)61 public LinkedBlockingDeque<SnippetEvent> getEventDeque(String qId) { 62 synchronized (mEventDeques) { 63 LinkedBlockingDeque<SnippetEvent> eventDeque = mEventDeques.get(qId); 64 if (eventDeque == null) { 65 eventDeque = new LinkedBlockingDeque<>(EVENT_DEQUE_MAX_SIZE); 66 mEventDeques.put(qId, eventDeque); 67 } 68 return eventDeque; 69 } 70 } 71 72 /** 73 * Post an {@link SnippetEvent} object to the Event cache. 74 * 75 * <p>Snippet classes should use this method to post events. If EVENT_DEQUE_MAX_SIZE is reached, 76 * the oldest elements will be retired until the new event could be posted. 77 * 78 * @param snippetEvent The snippetEvent to post to {@link EventCache}. 79 */ postEvent(SnippetEvent snippetEvent)80 public void postEvent(SnippetEvent snippetEvent) { 81 String qId = getQueueId(snippetEvent.getCallbackId(), snippetEvent.getName()); 82 Deque<SnippetEvent> q = getEventDeque(qId); 83 synchronized (q) { 84 while (!q.offer(snippetEvent)) { 85 SnippetEvent retiredEvent = q.removeFirst(); 86 Log.v( 87 String.format( 88 Locale.US, 89 "Retired event %s due to deque reaching the size limit (%s).", 90 retiredEvent, 91 EVENT_DEQUE_MAX_SIZE)); 92 } 93 } 94 Log.v(String.format(Locale.US, "Posted event(%s)", qId)); 95 } 96 97 /** Clears all cached events. */ clearAll()98 public void clearAll() { 99 synchronized (mEventDeques) { 100 mEventDeques.clear(); 101 } 102 } 103 } 104