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}