1 // Copyright 2014 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.app.Activity; 8 import android.content.DialogInterface; 9 import android.os.Bundle; 10 import android.util.Log; 11 import android.view.LayoutInflater; 12 import android.view.View; 13 import android.widget.EditText; 14 import android.widget.TextView; 15 16 import androidx.appcompat.app.AlertDialog; 17 18 import org.chromium.net.CronetEngine; 19 import org.chromium.net.CronetException; 20 import org.chromium.net.UrlRequest; 21 import org.chromium.net.UrlResponseInfo; 22 23 import java.io.ByteArrayOutputStream; 24 import java.io.IOException; 25 import java.nio.ByteBuffer; 26 import java.nio.channels.Channels; 27 import java.nio.channels.WritableByteChannel; 28 import java.util.concurrent.Executor; 29 import java.util.concurrent.Executors; 30 31 /** 32 * Activity for managing the Cronet Sample. 33 */ 34 public class CronetSampleActivity extends Activity { 35 private static final String TAG = CronetSampleActivity.class.getSimpleName(); 36 37 private CronetEngine mCronetEngine; 38 39 private String mUrl; 40 private TextView mResultText; 41 private TextView mReceiveDataText; 42 43 class SimpleUrlRequestCallback extends UrlRequest.Callback { 44 private ByteArrayOutputStream mBytesReceived = new ByteArrayOutputStream(); 45 private WritableByteChannel mReceiveChannel = Channels.newChannel(mBytesReceived); 46 47 @Override onRedirectReceived( UrlRequest request, UrlResponseInfo info, String newLocationUrl)48 public void onRedirectReceived( 49 UrlRequest request, UrlResponseInfo info, String newLocationUrl) { 50 Log.i(TAG, "****** onRedirectReceived ******"); 51 request.followRedirect(); 52 } 53 54 @Override onResponseStarted(UrlRequest request, UrlResponseInfo info)55 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 56 Log.i(TAG, "****** Response Started ******"); 57 Log.i(TAG, "*** Headers Are *** " + info.getAllHeaders()); 58 59 request.read(ByteBuffer.allocateDirect(32 * 1024)); 60 } 61 62 @Override onReadCompleted( UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer)63 public void onReadCompleted( 64 UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) { 65 byteBuffer.flip(); 66 Log.i(TAG, "****** onReadCompleted ******" + byteBuffer); 67 68 try { 69 mReceiveChannel.write(byteBuffer); 70 } catch (IOException e) { 71 Log.i(TAG, "IOException during ByteBuffer read. Details: ", e); 72 } 73 byteBuffer.clear(); 74 request.read(byteBuffer); 75 } 76 77 @Override onSucceeded(UrlRequest request, UrlResponseInfo info)78 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 79 Log.i(TAG, "****** Request Completed, status code is " + info.getHttpStatusCode() 80 + ", total received bytes is " + info.getReceivedByteCount()); 81 82 final String receivedData = mBytesReceived.toString(); 83 final String url = info.getUrl(); 84 final String text = "Completed " + url + " (" + info.getHttpStatusCode() + ")"; 85 CronetSampleActivity.this.runOnUiThread(new Runnable() { 86 @Override 87 public void run() { 88 mResultText.setText(text); 89 mReceiveDataText.setText(receivedData); 90 promptForURL(url); 91 } 92 }); 93 } 94 95 @Override onFailed(UrlRequest request, UrlResponseInfo info, CronetException error)96 public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) { 97 Log.i(TAG, "****** onFailed, error is: " + error.getMessage()); 98 99 final String url = mUrl; 100 final String text = "Failed " + mUrl + " (" + error.getMessage() + ")"; 101 CronetSampleActivity.this.runOnUiThread(new Runnable() { 102 @Override 103 public void run() { 104 mResultText.setText(text); 105 promptForURL(url); 106 } 107 }); 108 } 109 } 110 111 @Override onCreate(final Bundle savedInstanceState)112 protected void onCreate(final Bundle savedInstanceState) { 113 super.onCreate(savedInstanceState); 114 setContentView(R.layout.activity_main); 115 mResultText = (TextView) findViewById(R.id.resultView); 116 mReceiveDataText = (TextView) findViewById(R.id.dataView); 117 mReceiveDataText.setOnClickListener(new View.OnClickListener() { 118 @Override 119 public void onClick(View v) { 120 promptForURL(mUrl); 121 } 122 }); 123 124 CronetEngine.Builder myBuilder = new CronetEngine.Builder(this); 125 myBuilder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024) 126 .enableHttp2(true) 127 .enableQuic(true); 128 129 mCronetEngine = myBuilder.build(); 130 131 String appUrl = (getIntent() != null ? getIntent().getDataString() : null); 132 if (appUrl == null) { 133 promptForURL("https://"); 134 } else { 135 startWithURL(appUrl); 136 } 137 } 138 promptForURL(String url)139 private void promptForURL(String url) { 140 Log.i(TAG, "No URL provided via intent, prompting user..."); 141 AlertDialog.Builder alert = new AlertDialog.Builder(this); 142 alert.setTitle("Enter a URL"); 143 LayoutInflater inflater = getLayoutInflater(); 144 View alertView = inflater.inflate(R.layout.dialog_url, null); 145 final EditText urlInput = (EditText) alertView.findViewById(R.id.urlText); 146 urlInput.setText(url); 147 final EditText postInput = (EditText) alertView.findViewById(R.id.postText); 148 alert.setView(alertView); 149 150 alert.setPositiveButton("Load", new DialogInterface.OnClickListener() { 151 @Override 152 public void onClick(DialogInterface dialog, int button) { 153 String url = urlInput.getText().toString(); 154 String postData = postInput.getText().toString(); 155 startWithURL(url, postData); 156 } 157 }); 158 alert.show(); 159 } 160 applyPostDataToUrlRequestBuilder( UrlRequest.Builder builder, Executor executor, String postData)161 private void applyPostDataToUrlRequestBuilder( 162 UrlRequest.Builder builder, Executor executor, String postData) { 163 if (postData != null && postData.length() > 0) { 164 builder.setHttpMethod("POST"); 165 builder.addHeader("Content-Type", "application/x-www-form-urlencoded"); 166 builder.setUploadDataProvider( 167 UploadDataProviders.create(postData.getBytes()), executor); 168 } 169 } 170 startWithURL(String url)171 private void startWithURL(String url) { 172 startWithURL(url, null); 173 } 174 startWithURL(String url, String postData)175 private void startWithURL(String url, String postData) { 176 Log.i(TAG, "Cronet started: " + url); 177 mUrl = url; 178 179 Executor executor = Executors.newSingleThreadExecutor(); 180 UrlRequest.Callback callback = new SimpleUrlRequestCallback(); 181 UrlRequest.Builder builder = mCronetEngine.newUrlRequestBuilder(url, callback, executor); 182 applyPostDataToUrlRequestBuilder(builder, executor, postData); 183 builder.build().start(); 184 } 185 186 // Starts writing NetLog to disk. startNetLog() should be called afterwards. startNetLog()187 private void startNetLog() { 188 mCronetEngine.startNetLogToFile(getCacheDir().getPath() + "/netlog.json", false); 189 } 190 191 // Stops writing NetLog to disk. Should be called after calling startNetLog(). 192 // NetLog can be downloaded afterwards via: 193 // adb root 194 // adb pull /data/data/org.chromium.cronet_sample_apk/cache/netlog.json 195 // netlog.json can then be viewed in a Chrome tab navigated to chrome://net-internals/#import stopNetLog()196 private void stopNetLog() { 197 mCronetEngine.stopNetLog(); 198 } 199 } 200