1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.historyreport;
6 
7 import static org.chromium.base.ThreadUtils.assertOnBackgroundThread;
8 
9 import androidx.annotation.VisibleForTesting;
10 
11 import org.chromium.base.Log;
12 import org.chromium.base.annotations.CalledByNative;
13 import org.chromium.base.annotations.JNINamespace;
14 import org.chromium.base.annotations.NativeMethods;
15 import org.chromium.base.task.PostTask;
16 import org.chromium.content_public.browser.UiThreadTaskTraits;
17 
18 import java.io.PrintWriter;
19 import java.util.concurrent.atomic.AtomicBoolean;
20 
21 /**
22  * Bridge class for calls between Java and C++.
23  */
24 @JNINamespace("history_report")
25 public class HistoryReportJniBridge implements SearchJniBridge {
26     private static final String TAG = "historyreport";
27 
28     private long mNativeHistoryReportJniBridge;
29     private DataChangeObserver mDataChangeObserver;
30     private final AtomicBoolean mStarted = new AtomicBoolean();
31 
32     @Override
init(DataChangeObserver observer)33     public boolean init(DataChangeObserver observer) {
34         // This is called in the deferred task, so we couldn't assertOnBackgroundThread();
35         assert mDataChangeObserver == null || mDataChangeObserver == observer;
36         if (observer == null) return false;
37         if (mNativeHistoryReportJniBridge != 0) return true;
38         mDataChangeObserver = observer;
39         PostTask.runSynchronously(UiThreadTaskTraits.DEFAULT, () -> {
40             mNativeHistoryReportJniBridge =
41                     HistoryReportJniBridgeJni.get().init(HistoryReportJniBridge.this);
42         });
43         if (mNativeHistoryReportJniBridge == 0) {
44             Log.w(TAG, "JNI bridge initialization unsuccessful.");
45             return false;
46         }
47 
48         Log.d(TAG, "JNI bridge initialization successful.");
49         mStarted.set(true);
50         return true;
51     }
52 
isInitialized()53     private boolean isInitialized() {
54         return mNativeHistoryReportJniBridge != 0 && mDataChangeObserver != null && mStarted.get();
55     }
56 
57     @VisibleForTesting
58     @Override
isStartedForTest()59     public boolean isStartedForTest() {
60         return mStarted.get();
61     }
62 
63     @Override
query(long lastSeqNo, int limit)64     public DeltaFileEntry[] query(long lastSeqNo, int limit) {
65         assertOnBackgroundThread();
66         if (!isInitialized()) {
67             Log.w(TAG, "query when JNI bridge not initialized");
68             return new DeltaFileEntry[0];
69         }
70         Log.d(TAG, "query %d %d", lastSeqNo, limit);
71         DeltaFileEntry[] result = HistoryReportJniBridgeJni.get().query(
72                 mNativeHistoryReportJniBridge, HistoryReportJniBridge.this, lastSeqNo, limit);
73         return result;
74     }
75 
76     @Override
trimDeltaFile(long seqNoLowerBound)77     public long trimDeltaFile(long seqNoLowerBound) {
78         assertOnBackgroundThread();
79         if (!isInitialized()) {
80             Log.w(TAG, "trimDeltaFile when JNI bridge not initialized");
81             return -1;
82         }
83         Log.d(TAG, "trimDeltaFile %d", seqNoLowerBound);
84         return HistoryReportJniBridgeJni.get().trimDeltaFile(
85                 mNativeHistoryReportJniBridge, HistoryReportJniBridge.this, seqNoLowerBound);
86     }
87 
88     @Override
getUsageReportsBatch(int batchSize)89     public UsageReport[] getUsageReportsBatch(int batchSize) {
90         assertOnBackgroundThread();
91         if (!isInitialized()) {
92             Log.w(TAG, "getUsageReportsBatch when JNI bridge not initialized");
93             return new UsageReport[0];
94         }
95         Log.d(TAG, "getUsageReportsBatch %d", batchSize);
96         return HistoryReportJniBridgeJni.get().getUsageReportsBatch(
97                 mNativeHistoryReportJniBridge, HistoryReportJniBridge.this, batchSize);
98     }
99 
100     @Override
removeUsageReports(UsageReport[] reports)101     public void removeUsageReports(UsageReport[] reports) {
102         assertOnBackgroundThread();
103         if (!isInitialized()) {
104             Log.w(TAG, "removeUsageReports when JNI bridge not initialized");
105             return;
106         }
107         String[] reportIds = new String[reports.length];
108         for (int i = 0; i < reports.length; ++i) {
109             reportIds[i] = reports[i].reportId;
110         }
111         HistoryReportJniBridgeJni.get().removeUsageReports(
112                 mNativeHistoryReportJniBridge, HistoryReportJniBridge.this, reportIds);
113     }
114 
115     @Override
clearUsageReports()116     public void clearUsageReports() {
117         assertOnBackgroundThread();
118         if (!isInitialized()) {
119             Log.w(TAG, "clearUsageReports when JNI bridge not initialized");
120             return;
121         }
122         HistoryReportJniBridgeJni.get().clearUsageReports(
123                 mNativeHistoryReportJniBridge, HistoryReportJniBridge.this);
124     }
125 
126     @Override
addHistoricVisitsToUsageReportsBuffer()127     public boolean addHistoricVisitsToUsageReportsBuffer() {
128         assertOnBackgroundThread();
129         if (!isInitialized()) {
130             Log.w(TAG, "addHistoricVisitsToUsageReportsBuffer when JNI bridge not initialized");
131             return false;
132         }
133         return HistoryReportJniBridgeJni.get().addHistoricVisitsToUsageReportsBuffer(
134                 mNativeHistoryReportJniBridge, HistoryReportJniBridge.this);
135     }
136 
137     @Override
dump(PrintWriter writer)138     public void dump(PrintWriter writer) {
139         writer.append("\nHistoryReportJniBridge [").append("started: " + mStarted.get())
140                 .append(", initialized: " + isInitialized());
141         if (isInitialized()) {
142             writer.append(", "
143                     + HistoryReportJniBridgeJni.get().dump(
144                             mNativeHistoryReportJniBridge, HistoryReportJniBridge.this));
145         }
146         writer.append("]");
147     }
148 
149     @CalledByNative
createDeltaFileEntriesArray(int size)150     private static DeltaFileEntry[] createDeltaFileEntriesArray(int size) {
151         return new DeltaFileEntry[size];
152     }
153 
154     @CalledByNative
setDeltaFileEntry(DeltaFileEntry[] entries, int position, long seqNo, String type, String id, String url, int score, String title, String indexedUrl)155     private static void setDeltaFileEntry(DeltaFileEntry[] entries, int position, long seqNo,
156             String type, String id, String url, int score, String title, String indexedUrl) {
157         entries[position] = new DeltaFileEntry(seqNo, type, id, url, score, title, indexedUrl);
158     }
159 
160     @CalledByNative
createUsageReportsArray(int size)161     private static UsageReport[] createUsageReportsArray(int size) {
162         return new UsageReport[size];
163     }
164 
165     @CalledByNative
setUsageReport(UsageReport[] reports, int position, String reportId, String pageId, long timestampMs, boolean typedVisit)166     private static void setUsageReport(UsageReport[] reports, int position, String reportId,
167             String pageId, long timestampMs, boolean typedVisit) {
168         reports[position] = new UsageReport(reportId, pageId, timestampMs, typedVisit);
169     }
170 
171     @CalledByNative
onDataChanged()172     private void onDataChanged() {
173         Log.d(TAG, "onDataChanged");
174         mDataChangeObserver.onDataChanged();
175     }
176 
177     @CalledByNative
onDataCleared()178     private void onDataCleared() {
179         Log.d(TAG, "onDataCleared");
180         mDataChangeObserver.onDataCleared();
181     }
182 
183     @CalledByNative
startReportingTask()184     private void startReportingTask() {
185         Log.d(TAG, "startReportingTask");
186         mDataChangeObserver.startReportingTask();
187     }
188 
189     @CalledByNative
stopReportingTask()190     private void stopReportingTask() {
191         Log.d(TAG, "stopReportingTask");
192         mDataChangeObserver.stopReportingTask();
193     }
194 
195     @NativeMethods
196     interface Natives {
init(HistoryReportJniBridge caller)197         long init(HistoryReportJniBridge caller);
trimDeltaFile(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller, long seqNoLowerBound)198         long trimDeltaFile(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller,
199                 long seqNoLowerBound);
query(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller, long lastSeqNo, int limit)200         DeltaFileEntry[] query(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller,
201                 long lastSeqNo, int limit);
getUsageReportsBatch( long nativeHistoryReportJniBridge, HistoryReportJniBridge caller, int batchSize)202         UsageReport[] getUsageReportsBatch(
203                 long nativeHistoryReportJniBridge, HistoryReportJniBridge caller, int batchSize);
removeUsageReports(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller, String[] reportIds)204         void removeUsageReports(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller,
205                 String[] reportIds);
clearUsageReports(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller)206         void clearUsageReports(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller);
addHistoricVisitsToUsageReportsBuffer( long nativeHistoryReportJniBridge, HistoryReportJniBridge caller)207         boolean addHistoricVisitsToUsageReportsBuffer(
208                 long nativeHistoryReportJniBridge, HistoryReportJniBridge caller);
dump(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller)209         String dump(long nativeHistoryReportJniBridge, HistoryReportJniBridge caller);
210     }
211 }
212