1 // Copyright 2023 The Chromium Authors
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.cronet_sample_apk;
6 
7 import android.os.Bundle;
8 import android.os.Handler;
9 import android.os.Looper;
10 import android.view.LayoutInflater;
11 import android.view.View;
12 import android.view.ViewGroup;
13 import android.widget.Button;
14 import android.widget.EditText;
15 import android.widget.TextView;
16 
17 import androidx.annotation.NonNull;
18 import androidx.annotation.Nullable;
19 import androidx.fragment.app.Fragment;
20 import androidx.fragment.app.FragmentActivity;
21 import androidx.lifecycle.ViewModelProvider;
22 
23 import org.chromium.base.Log;
24 import org.chromium.net.CronetEngine;
25 import org.chromium.net.CronetException;
26 import org.chromium.net.UrlRequest;
27 import org.chromium.net.UrlResponseInfo;
28 
29 import java.io.ByteArrayOutputStream;
30 import java.io.IOException;
31 import java.nio.ByteBuffer;
32 import java.nio.channels.Channels;
33 import java.nio.channels.WritableByteChannel;
34 import java.util.concurrent.Executor;
35 import java.util.concurrent.Executors;
36 
37 public class MainFragment extends Fragment {
38     private static final String TAG = "CronetSampleApp";
39     private EditText mUrlEditText;
40     private TextView mResultText;
41     private Button mStartButton;
42     private Button mResetEngineButton;
43     private Button mClearTextButton;
44     private SampleActivityViewModel mActivityViewModel;
45 
getCronetEngine()46     private CronetEngine getCronetEngine() {
47         return ((CronetSampleApplication) requireActivity().getApplication()).getCronetEngine();
48     }
49 
resetEngine()50     private void resetEngine() {
51         ((CronetSampleApplication) requireActivity().getApplication()).restartCronetEngine();
52     }
53 
init(View view)54     private void init(View view) {
55         mUrlEditText = view.findViewById(R.id.url_edittext);
56         mResultText = view.findViewById(R.id.result_textview);
57         mStartButton = view.findViewById(R.id.start_button);
58         mResetEngineButton = view.findViewById(R.id.reset_button);
59         mClearTextButton = view.findViewById(R.id.clear_button);
60         mStartButton.setOnClickListener(
61                 v -> {
62                     Executor executor = Executors.newSingleThreadExecutor();
63                     UrlRequest.Callback callback = new SimpleUrlRequestCallback();
64                     UrlRequest.Builder builder =
65                             getCronetEngine()
66                                     .newUrlRequestBuilder(
67                                             mUrlEditText.getText().toString(), callback, executor);
68                     builder.build().start();
69                 });
70 
71         mResetEngineButton.setOnClickListener(v -> resetEngine());
72         mClearTextButton.setOnClickListener(v -> mResultText.setText(""));
73         mActivityViewModel =
74                 new ViewModelProvider((FragmentActivity) requireActivity())
75                         .get(SampleActivityViewModel.class);
76     }
77 
78     @Nullable
79     @Override
onCreateView( @onNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)80     public View onCreateView(
81             @NonNull LayoutInflater inflater,
82             @Nullable ViewGroup container,
83             @Nullable Bundle savedInstanceState) {
84         View view = inflater.inflate(R.layout.main_fragment, container, false);
85         init(view);
86         return view;
87     }
88 
89     class SimpleUrlRequestCallback extends UrlRequest.Callback {
90         private ByteArrayOutputStream mBytesReceived = new ByteArrayOutputStream();
91         private WritableByteChannel mReceiveChannel = Channels.newChannel(mBytesReceived);
92 
93         @Override
onRedirectReceived( UrlRequest request, UrlResponseInfo info, String newLocationUrl)94         public void onRedirectReceived(
95                 UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
96             Log.i(TAG, "****** onRedirectReceived ******");
97             request.followRedirect();
98         }
99 
100         @Override
onResponseStarted(UrlRequest request, UrlResponseInfo info)101         public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
102             Log.i(TAG, "****** Response Started ******");
103             Log.i(TAG, "*** Headers Are *** " + info.getAllHeaders());
104             if (Options.isBooleanOptionOn(Options.OptionsIdentifier.SLOW_DOWNLOAD)) {
105                 try {
106                     Thread.sleep(10000);
107                 } catch (InterruptedException e) {
108                     Thread.currentThread().interrupt();
109                     throw new RuntimeException(e);
110                 }
111             }
112             request.read(ByteBuffer.allocateDirect(32 * 1024));
113         }
114 
115         @Override
onReadCompleted( UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer)116         public void onReadCompleted(
117                 UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
118             byteBuffer.flip();
119             Log.i(TAG, "****** onReadCompleted ******" + byteBuffer);
120 
121             try {
122                 mReceiveChannel.write(byteBuffer);
123             } catch (IOException e) {
124                 Log.i(TAG, "IOException during ByteBuffer read. Details: ", e);
125             }
126             byteBuffer.clear();
127             request.read(byteBuffer);
128             if (Options.isBooleanOptionOn(Options.OptionsIdentifier.SLOW_DOWNLOAD)) {
129                 try {
130                     Thread.sleep(10000);
131                 } catch (InterruptedException e) {
132                     Thread.currentThread().interrupt();
133                     throw new RuntimeException(e);
134                 }
135             }
136         }
137 
138         @Override
onSucceeded(UrlRequest request, UrlResponseInfo info)139         public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
140             Log.i(
141                     TAG,
142                     "****** Request Completed, status code is "
143                             + info.getHttpStatusCode()
144                             + ", total received bytes is "
145                             + info.getReceivedByteCount());
146             final String receivedData = mBytesReceived.toString();
147             final String url = info.getUrl();
148             final String text = "Completed " + url + " (" + info.getHttpStatusCode() + ")";
149             new Handler(Looper.getMainLooper())
150                     .post(() -> mResultText.setText(String.format("%s\n%s", text, receivedData)));
151         }
152 
153         @Override
onFailed(UrlRequest request, UrlResponseInfo info, CronetException error)154         public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
155             Log.i(TAG, "****** onFailed, error is: " + error.getMessage());
156             final String text = "Failed " + " (" + error.getMessage() + ")";
157             new Handler(Looper.getMainLooper())
158                     .post(() -> mResultText.setText(String.format("%s", text)));
159         }
160     }
161 
162     // Starts writing NetLog to disk. startNetLog() should be called afterwards.
startNetLog()163     private void startNetLog() {
164         getCronetEngine()
165                 .startNetLogToFile(
166                         requireActivity().getCacheDir().getPath() + "/netlog.json", false);
167     }
168 
169     // Stops writing NetLog to disk. Should be called after calling startNetLog().
170     // NetLog can be downloaded afterwards via:
171     //   adb root
172     //   adb pull /data/data/org.chromium.cronet_sample_apk/cache/netlog.json
173     // netlog.json can then be viewed in a Chrome tab navigated to chrome://net-internals/#import
stopNetLog()174     private void stopNetLog() {
175         getCronetEngine().stopNetLog();
176     }
177 }
178