JevReviewLedger.java
原始文件
1package com.android.commands.monkey.ape.jev;
2
3import java.util.HashMap;
4import java.util.Map;
5import java.util.Collections;
6import java.util.HashSet;
7import java.util.Set;
8import com.android.commands.monkey.ape.model.State;
9import com.android.commands.monkey.ape.model.ModelAction;
10import com.android.commands.monkey.ape.tree.GUITreeAction;
11
12/** Run-local review decisions shared by synchronous and asynchronous paths. */
13public final class JevReviewLedger {
14 public static final class Decision {
15 public final String outcome;
16 public final Double confidence;
17 public final String representativeModelActionId;
18 public final String inputFingerprint;
19 public final String requestId;
20 public final String candidateId;
21 // Navigation-only, run-local receipt. An arrival never consumes PROMOTE.
22 private boolean navigationArrived;
23 Decision(String value,Double score,String representative,String fingerprint) {
24 this(value,score,representative,fingerprint,null);
25 }
26 Decision(String value,Double score,String representative,String fingerprint,String request) {
27 this(value,score,representative,fingerprint,request,null);
28 }
29 Decision(String value,Double score,String representative,String fingerprint,String request,String candidate) {
30 outcome=value;confidence=score;representativeModelActionId=representative;inputFingerprint=fingerprint;requestId=request;candidateId=candidate;
31 }
32 }
33 static final class PairResult {
34 final String choice; final double confidence; final org.json.JSONObject probabilities;
35 PairResult(org.json.JSONObject answer)throws org.json.JSONException {
36 choice=answer.getString("choice"); confidence=answer.getDouble("confidence");
37 probabilities=answer.has("probabilities")?new org.json.JSONObject(answer.getJSONObject("probabilities").toString()):null;
38 }
39 org.json.JSONObject json()throws org.json.JSONException {
40 org.json.JSONObject out=new org.json.JSONObject().put("choice",choice).put("confidence",confidence);
41 if(probabilities!=null)out.put("probabilities",new org.json.JSONObject(probabilities.toString()));return out;
42 }
43 }
44 private static final class Key {
45 final String contract;
46 final String activityScope;
47 final ConcreteActionIdentity identity;
48 Key(String c,String scope,ConcreteActionIdentity i){contract=c;activityScope=scope==null?"":scope;identity=i;}
49 public int hashCode(){return 31*(31*contract.hashCode()+activityScope.hashCode())+identity.hashCode();}
50 public boolean equals(Object other){
51 if(!(other instanceof Key))return false;
52 Key value=(Key)other;return contract.equals(value.contract)&&activityScope.equals(value.activityScope)&&identity.equals(value.identity);
53 }
54 }
55 private final java.util.concurrent.locks.ReentrantLock budgetLock=new java.util.concurrent.locks.ReentrantLock();
56 private Map<Key,Decision> decisions=new HashMap<Key,Decision>();
57 private Map<String,Map<ConcreteActionIdentity,Decision>> fingerprints=new HashMap<String,Map<ConcreteActionIdentity,Decision>>();
58 private Map<String,PairResult> pairResults=new HashMap<String,PairResult>();
59 private final Map<Key,Set<String>> guiBindings=new HashMap<Key,Set<String>>();
60 private final Map<Key,Long> bindingModelVersions=new HashMap<Key,Long>();
61 private final Map<Key,Long> bindingRevisions=new HashMap<Key,Long>();
62 private final Map<Key,Integer> bindingConfirmationCounts=new HashMap<Key,Integer>();
63 private final Map<Key,GUITreeAction> sources=new HashMap<Key,GUITreeAction>();
64 private final Map<Key,State> sourceStates=new HashMap<Key,State>();
65 static final class Staged {
66 final Decision decision;final String contract,scope;final GUITreeAction source;
67 Staged(Key key,Decision value,GUITreeAction origin){decision=value;contract=key.contract;scope=key.activityScope;source=origin;}
68 }
69 static final class Update {
70 final Key key;final Decision decision;
71 Decision previous; // Audit of an actual same-key replacement, populated by the existing transaction.
72 Update(String contract,String scope,ConcreteActionIdentity identity,String outcome,Double confidence,String representative,String fingerprint,String request) {
73 this(contract,scope,identity,outcome,confidence,representative,fingerprint,request,null);
74 }
75 Update(String contract,String scope,ConcreteActionIdentity identity,String outcome,Double confidence,String representative,String fingerprint,String request,String candidate) {
76 key=new Key(contract,scope,identity);decision=new Decision(outcome,confidence,representative,fingerprint,request,candidate);
77 }
78 }
79 /** Build privately; publish only while the caller's original deadline is live. */
80 boolean putBeforeDeadline(java.util.List<Update> updates,long deadline) {JevDeadline.lock(budgetLock);try {
81 Map<Key,Decision> next=new HashMap<Key,Decision>();
82 for(Map.Entry<Key,Decision> e:decisions.entrySet()) {
83 if(System.nanoTime()>=deadline)return false;
84 next.put(e.getKey(),e.getValue());
85 }
86 Map<String,Map<ConcreteActionIdentity,Decision>> fps=new HashMap<String,Map<ConcreteActionIdentity,Decision>>();
87 for(Map.Entry<String,Map<ConcreteActionIdentity,Decision>> e:fingerprints.entrySet()) {
88 if(System.nanoTime()>=deadline)return false;
89 fps.put(e.getKey(),e.getValue());
90 }
91 Set<String> copied=new HashSet<String>();
92 for(Update u:updates) {
93 if(System.nanoTime()>=deadline)return false;
94 u.previous=next.put(u.key,u.decision);
95 if(u.decision.inputFingerprint!=null){String k=scopeKey(u.key.contract,u.key.activityScope,u.decision.inputFingerprint);
96 Map<ConcreteActionIdentity,Decision> values=fps.get(k);
97 if(copied.add(k)) {
98 Map<ConcreteActionIdentity,Decision> privateValues=new HashMap<ConcreteActionIdentity,Decision>();
99 if(values!=null)for(Map.Entry<ConcreteActionIdentity,Decision> e:values.entrySet()) {
100 if(System.nanoTime()>=deadline)return false;
101 privateValues.put(e.getKey(),e.getValue());
102 }
103 values=privateValues;fps.put(k,values);
104 }
105 values.put(u.key.identity,u.decision);}
106 }
107 if(System.nanoTime()>=deadline || Thread.currentThread().isInterrupted())return false;
108 decisions=next;fingerprints=fps;return true;
109 } finally {budgetLock.unlock();}}
110 public Decision get(String contract,ConcreteActionIdentity identity) {JevDeadline.lock(budgetLock);try {
111 return get(contract,"",identity);
112 } finally {budgetLock.unlock();}}
113 public Decision get(String contract,String activityScope,ConcreteActionIdentity identity) {JevDeadline.lock(budgetLock);try {return decisions.get(new Key(contract,activityScope,identity));} finally {budgetLock.unlock();}}
114 public void put(String contract,ConcreteActionIdentity identity,String outcome,Double confidence,
115 String representativeModelActionId,String inputFingerprint) {JevDeadline.lock(budgetLock);try {
116 put(contract,"",identity,outcome,confidence,representativeModelActionId,inputFingerprint);
117 } finally {budgetLock.unlock();}}
118 public void put(String contract,String activityScope,ConcreteActionIdentity identity,String outcome,Double confidence,
119 String representativeModelActionId,String inputFingerprint) {JevDeadline.lock(budgetLock);try {
120 put(contract,activityScope,identity,outcome,confidence,representativeModelActionId,inputFingerprint,null);
121 } finally {budgetLock.unlock();}}
122 public void put(String contract,String activityScope,ConcreteActionIdentity identity,String outcome,Double confidence,
123 String representativeModelActionId,String inputFingerprint,String requestId) {JevDeadline.lock(budgetLock);try {
124 if("REVIEW_FAILED".equals(outcome)||"IN_FLIGHT".equals(outcome))return;
125 Decision decision=new Decision(outcome,confidence,representativeModelActionId,inputFingerprint,requestId);
126 decisions.put(new Key(contract,activityScope,identity),decision);
127 if(inputFingerprint!=null) {
128 String key=scopeKey(contract,activityScope,inputFingerprint);Map<ConcreteActionIdentity,Decision> values=fingerprints.get(key);
129 if(values==null){values=new HashMap<ConcreteActionIdentity,Decision>();fingerprints.put(key,values);}values.put(identity,decision);
130 }
131 } finally {budgetLock.unlock();}}
132 public Map<ConcreteActionIdentity,Decision> byFingerprint(String contract,String inputFingerprint) {JevDeadline.lock(budgetLock);try {
133 return byFingerprint(contract,"",inputFingerprint);
134 } finally {budgetLock.unlock();}}
135 public Map<ConcreteActionIdentity,Decision> byFingerprint(String contract,String activityScope,String inputFingerprint) {JevDeadline.lock(budgetLock);try {
136 Map<ConcreteActionIdentity,Decision> values=fingerprints.get(scopeKey(contract,activityScope,inputFingerprint));
137 return values==null?Collections.<ConcreteActionIdentity,Decision>emptyMap():Collections.unmodifiableMap(new HashMap<ConcreteActionIdentity,Decision>(values));
138 } finally {budgetLock.unlock();}}
139 public int size(){JevDeadline.lock(budgetLock);try {return decisions.size();} finally {budgetLock.unlock();}}
140 Map<String,PairResult> pairResults(java.util.Collection<String> keys) {JevDeadline.lock(budgetLock);try {
141 Map<String,PairResult> out=new HashMap<String,PairResult>();for(String key:keys){PairResult value=pairResults.get(key);if(value==null)return Collections.emptyMap();out.put(key,value);}return out;
142 } finally {budgetLock.unlock();}}
143 void putPairResults(Map<String,PairResult> values) {JevDeadline.lock(budgetLock);try {pairResults.putAll(values);} finally {budgetLock.unlock();}}
144 /** Read the existing ledger at close; no new pending-Probe inventory or terminal inference. */
145 java.util.List<org.json.JSONObject> snapshot(String contract) {JevDeadline.lock(budgetLock);try {
146 java.util.List<org.json.JSONObject> rows=new java.util.ArrayList<org.json.JSONObject>();
147 for(Map.Entry<Key,Decision> entry:decisions.entrySet()) {
148 Key key=entry.getKey();Decision d=entry.getValue();
149 if(!key.contract.equals(contract)||d.requestId==null||d.candidateId==null)continue;
150 rows.add(JevRegistry.fields("entry_id",d.requestId+"-"+d.candidateId,"request_id",d.requestId,
151 "candidate_id",d.candidateId,"decision_role","candidate_decision","ledger_status",d.outcome,
152 "input_fingerprint",d.inputFingerprint,"concrete_identity",JevEvidence.digest(key.identity.toString()),
153 "decision_scope",JevEvidence.digest(key.activityScope)));
154 }
155 return rows;
156 } finally {budgetLock.unlock();}}
157 public void bind(String contract,ConcreteActionIdentity identity,String guiIdentity) {JevDeadline.lock(budgetLock);try {
158 bind(contract,"",identity,guiIdentity);
159 } finally {budgetLock.unlock();}}
160 public void bind(String contract,String activityScope,ConcreteActionIdentity identity,String guiIdentity) {JevDeadline.lock(budgetLock);try {
161 bind(contract,activityScope,identity,guiIdentity,null,null);
162 } finally {budgetLock.unlock();}}
163 public void bind(String contract,String activityScope,ConcreteActionIdentity identity,String guiIdentity,Long modelVersion,Long revision) {JevDeadline.lock(budgetLock);try {
164 bind(contract,activityScope,identity,guiIdentity,modelVersion,revision,null);
165 } finally {budgetLock.unlock();}}
166 public void bind(String contract,String activityScope,ConcreteActionIdentity identity,String guiIdentity,Long modelVersion,Long revision,Integer confirmations) {JevDeadline.lock(budgetLock);try {
167 if(guiIdentity==null||guiIdentity.length()==0)return;
168 Key key=new Key(contract,activityScope,identity);Set<String> values=guiBindings.get(key);
169 if(values==null){values=new HashSet<String>();guiBindings.put(key,values);}values.add(guiIdentity);
170 if(modelVersion!=null)bindingModelVersions.put(key,modelVersion);
171 if(revision!=null)bindingRevisions.put(key,revision);
172 if(confirmations!=null)bindingConfirmationCounts.put(key,confirmations);
173 } finally {budgetLock.unlock();}}
174 public Set<String> bindings(String contract,ConcreteActionIdentity identity) {JevDeadline.lock(budgetLock);try {
175 return bindings(contract,"",identity);
176 } finally {budgetLock.unlock();}}
177 public Set<String> bindings(String contract,String activityScope,ConcreteActionIdentity identity) {JevDeadline.lock(budgetLock);try {
178 Set<String> values=guiBindings.get(new Key(contract,activityScope,identity));
179 return values==null?Collections.<String>emptySet():Collections.unmodifiableSet(new HashSet<String>(values));
180 } finally {budgetLock.unlock();}}
181 /** Associate the existing decision key with its native source, not a second Probe inventory. */
182 void bindSource(String contract,String scope,ConcreteActionIdentity identity,String gui,long version,long revision,int confirmations,GUITreeAction source){
183 JevDeadline.lock(budgetLock);try {
184 bind(contract,scope,identity,gui,version,revision,confirmations);
185 Key key=new Key(contract,scope,identity);sources.put(key,source);sourceStates.put(key,source.getGUITree().getCurrentState());
186 } finally {budgetLock.unlock();}
187 }
188 private static boolean reviewContract(String value,String base){
189 return value.equals(base)||value.equals(base+":"+JevHistoryPairwiseProtocol.EVIDENCE_VIEW_VERSION)
190 ||value.equals(base+":"+JevHistoryPairwiseProtocol.INCREMENTAL_VIEW_VERSION);
191 }
192 static final class PendingProbe {
193 private final Key key;
194 final Decision decision;final GUITreeAction source;final ConcreteActionIdentity identity;
195 final boolean navigationArrived;
196 PendingProbe(Key k,Decision d,GUITreeAction s){key=k;decision=d;source=s;identity=k.identity;navigationArrived=d.navigationArrived;}
197 }
198 /** Read only accepted source-bound decisions; no GUI realization or evidence construction. */
199 java.util.List<PendingProbe> pendingProbes(String contract,State state) {
200 JevDeadline.lock(budgetLock);try {
201 java.util.List<PendingProbe> out=new java.util.ArrayList<PendingProbe>();
202 for(Map.Entry<Key,Decision> entry:decisions.entrySet()) {
203 Key key=entry.getKey();Decision d=entry.getValue();
204 if(reviewContract(key.contract,contract)&&sourceStates.get(key)==state&&"PROBE".equals(d.outcome))
205 out.add(new PendingProbe(key,d,sources.get(key)));
206 }
207 return out;
208 } finally {budgetLock.unlock();}
209 }
210 /** Necessary condition only; staged/executable retain all binding checks. */
211 boolean hasPendingProbe(String contract,State state) {
212 long examined=0;
213 JevDeadline.lock(budgetLock);try {
214 for(Map.Entry<Key,Decision> entry:decisions.entrySet()) {
215 JevDeadline.check();examined++;
216 if(reviewContract(entry.getKey().contract,contract)&&sourceStates.get(entry.getKey())==state
217 &&"PROBE".equals(entry.getValue().outcome))return true;
218 }
219 return false;
220 } finally {budgetLock.unlock();JevReviewProfile.add("pending_probe_entries",0,examined);}
221 }
222 void navigationArrived(PendingProbe probe) {
223 JevDeadline.lock(budgetLock);try {
224 if(decisions.get(probe.key)==probe.decision)probe.decision.navigationArrived=true;
225 } finally {budgetLock.unlock();}
226 }
227 /** PROMOTE is a run-local pending execution opportunity. History/model
228 revisions invalidate raw model work, not this already accepted result.
229 Other outcomes retain the existing GUI/revision cache policy. */
230 Staged staged(String contract,State state,ModelAction action,ConcreteActionIdentity identity,
231 String gui,long version,long revision,int confirmations){
232 long profileStart=JevReviewProfile.start(),examined=0;
233 JevDeadline.lock(budgetLock);try {
234 Staged found=null;
235 for(Map.Entry<Key,Decision> entry:decisions.entrySet()){
236 examined++;
237 JevDeadline.check();Key key=entry.getKey();Decision value=entry.getValue();GUITreeAction origin=sources.get(key);
238 if(!reviewContract(key.contract,contract)||!key.identity.equals(identity)||origin==null||sourceStates.get(key)!=state)continue;
239 if(origin.getGUITree().getCurrentState()!=state || origin.getModelAction()==null || origin.getModelAction().getState()!=state
240 ||origin.getActionType()!=action.getType()||!origin.getModelAction().getTarget().equals(action.getTarget()))continue;
241 if("PROBE".equals(value.outcome)||"EXECUTED".equals(value.outcome)){
242 if(found==null || "EXECUTED".equals(value.outcome) || ("PROBE".equals(value.outcome)&&!"EXECUTED".equals(found.decision.outcome)))found=new Staged(key,value,origin);
243 continue;
244 }
245 Set<String> bound=guiBindings.get(key);Long mv=bindingModelVersions.get(key),rv=bindingRevisions.get(key);
246 Integer cc=bindingConfirmationCounts.get(key);
247 if(bound==null||!bound.contains(gui)||mv==null||mv!=version||rv==null||rv!=revision)continue;
248 if(found==null)found=new Staged(key,value,origin);
249 }
250 return found;
251 } finally {budgetLock.unlock();JevReviewProfile.end("ledger_staged",profileStart);JevReviewProfile.add("staged_entries",0,examined);}
252 }
253 /** Promote every contract-local decision for a confirmed native execution. */
254 public void markExecuted(ConcreteActionIdentity identity) {JevDeadline.lock(budgetLock);try {
255 markExecuted(null,identity);
256 } finally {budgetLock.unlock();}}
257 /** Promote decisions only within the confirmed Activity scope. */
258 public void markExecuted(String activityScope,ConcreteActionIdentity identity) {JevDeadline.lock(budgetLock);try {
259 for(Map.Entry<Key,Decision> entry:new HashMap<Key,Decision>(decisions).entrySet()) {
260 if(!entry.getKey().identity.equals(identity) || (activityScope!=null && !entry.getKey().activityScope.equals(activityScope)))continue;
261 Decision old=entry.getValue(),updated=new Decision("EXECUTED",old.confidence,old.representativeModelActionId,old.inputFingerprint,old.requestId,old.candidateId);
262 decisions.put(entry.getKey(),updated);
263 if(old.inputFingerprint!=null) {
264 String fingerprint=scopeKey(entry.getKey().contract,entry.getKey().activityScope,old.inputFingerprint);Map<ConcreteActionIdentity,Decision> values=fingerprints.get(fingerprint);
265 if(values!=null)values.put(identity,updated);
266 }
267 }
268 } finally {budgetLock.unlock();}}
269 /** Only a confirmed execution in this native State can finish its pending decisions. */
270 void markExecutedMatching(String contract,State state,ConcreteActionIdentity identity){
271 JevDeadline.lock(budgetLock);try {
272 for(Map.Entry<Key,Decision> entry:new HashMap<Key,Decision>(decisions).entrySet()){
273 Key key=entry.getKey();
274 if((contract!=null&&!reviewContract(key.contract,contract))||sourceStates.get(key)!=state||!key.identity.equals(identity))continue;
275 Decision old=entry.getValue(),done=new Decision("EXECUTED",old.confidence,old.representativeModelActionId,old.inputFingerprint,old.requestId,old.candidateId);
276 decisions.put(key,done);
277 if(old.inputFingerprint!=null){Map<ConcreteActionIdentity,Decision> values=fingerprints.get(scopeKey(key.contract,key.activityScope,old.inputFingerprint));if(values!=null)values.put(identity,done);}
278 }
279 } finally {budgetLock.unlock();}
280 }
281 private static String scopeKey(String contract,String activityScope,String fingerprint) {
282 return contract+"|"+(activityScope==null?"":activityScope)+"|"+fingerprint;
283 }
284}