blob: 67eec21c215c13eb2338a4254380f7e073f51799 [file] [log] [blame]
Selim Gurunc8c82f62019-04-17 14:07:30 -07001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.car;
18
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -070019import static android.car.CarBugreportManager.CarBugreportManagerCallback.CAR_BUGREPORT_DUMPSTATE_CONNECTION_FAILED;
20import static android.car.CarBugreportManager.CarBugreportManagerCallback.CAR_BUGREPORT_DUMPSTATE_FAILED;
21
22import android.annotation.NonNull;
23import android.annotation.Nullable;
Selim Gurunc8c82f62019-04-17 14:07:30 -070024import android.annotation.RequiresPermission;
25import android.car.CarBugreportManager.CarBugreportManagerCallback;
26import android.car.ICarBugreportCallback;
27import android.car.ICarBugreportService;
28import android.content.Context;
Selim Gurun606b2ec2019-05-24 22:47:47 -070029import android.content.pm.PackageManager;
Selim Gurunc8c82f62019-04-17 14:07:30 -070030import android.net.LocalSocket;
31import android.net.LocalSocketAddress;
Selim Gurun606b2ec2019-05-24 22:47:47 -070032import android.os.Binder;
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -070033import android.os.Build;
Selim Gurunc8c82f62019-04-17 14:07:30 -070034import android.os.Handler;
35import android.os.HandlerThread;
36import android.os.ParcelFileDescriptor;
Selim Gurun606b2ec2019-05-24 22:47:47 -070037import android.os.Process;
Selim Gurunc8c82f62019-04-17 14:07:30 -070038import android.os.RemoteException;
39import android.os.SystemClock;
40import android.os.SystemProperties;
Felipe Leme176a5fd2021-01-20 15:48:33 -080041import android.util.IndentingPrintWriter;
Selim Gurunc8c82f62019-04-17 14:07:30 -070042import android.util.Slog;
43
44import com.android.internal.annotations.GuardedBy;
Zhomart Mukhamejanov3740edc2021-06-15 16:41:42 -070045import com.android.internal.annotations.VisibleForTesting;
Selim Gurunc8c82f62019-04-17 14:07:30 -070046
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -070047import java.io.BufferedReader;
Selim Gurunc8c82f62019-04-17 14:07:30 -070048import java.io.DataInputStream;
49import java.io.DataOutputStream;
50import java.io.IOException;
51import java.io.InputStream;
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -070052import java.io.InputStreamReader;
Selim Gurunc8c82f62019-04-17 14:07:30 -070053import java.io.OutputStream;
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -070054import java.util.concurrent.atomic.AtomicBoolean;
Selim Gurunc8c82f62019-04-17 14:07:30 -070055
Selim Gurunc8c82f62019-04-17 14:07:30 -070056/**
Selim Guruna85b7e72019-06-07 11:01:42 -070057 * Bugreport service for cars.
Selim Gurunc8c82f62019-04-17 14:07:30 -070058 */
59public class CarBugreportManagerService extends ICarBugreportService.Stub implements
60 CarServiceBase {
61
Mayank Garg72c71d22021-02-03 23:54:45 -080062 private static final String TAG = CarLog.tagFor(CarBugreportManagerService.class);
Selim Gurunc8c82f62019-04-17 14:07:30 -070063
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -070064 /**
65 * {@code dumpstate} progress prefixes.
66 *
67 * <p>The protocol is described in {@code frameworks/native/cmds/bugreportz/readme.md}.
68 */
69 private static final String BEGIN_PREFIX = "BEGIN:";
70 private static final String PROGRESS_PREFIX = "PROGRESS:";
71 private static final String OK_PREFIX = "OK:";
72 private static final String FAIL_PREFIX = "FAIL:";
Selim Gurunc8c82f62019-04-17 14:07:30 -070073
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -070074 /**
Zhomart Mukhamejanov6180b8c2021-03-04 12:03:46 -080075 * The services are defined in {@code packages/services/Car/cpp/bugreport/carbugreportd.rc}.
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -070076 */
Zhomart Mukhamejanov6180b8c2021-03-04 12:03:46 -080077 private static final String BUGREPORTD_SERVICE = "carbugreportd";
78 private static final String DUMPSTATEZ_SERVICE = "cardumpstatez";
Selim Gurunc8c82f62019-04-17 14:07:30 -070079
Selim Gurunc4d7c012019-05-13 20:27:22 -070080 // The socket definitions must match the actual socket names defined in car_bugreportd service
81 // definition.
82 private static final String BUGREPORT_PROGRESS_SOCKET = "car_br_progress_socket";
83 private static final String BUGREPORT_OUTPUT_SOCKET = "car_br_output_socket";
Selim Guruna85b7e72019-06-07 11:01:42 -070084 private static final String BUGREPORT_EXTRA_OUTPUT_SOCKET = "car_br_extra_output_socket";
Selim Gurunc4d7c012019-05-13 20:27:22 -070085
Selim Gurunc8c82f62019-04-17 14:07:30 -070086 private static final int SOCKET_CONNECTION_MAX_RETRY = 10;
Selim Guruna85b7e72019-06-07 11:01:42 -070087 private static final int SOCKET_CONNECTION_RETRY_DELAY_IN_MS = 5000;
Selim Gurunc8c82f62019-04-17 14:07:30 -070088
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -070089 private final Context mContext;
Zhomart Mukhamejanov3740edc2021-06-15 16:41:42 -070090 private final boolean mIsUserBuild;
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -070091 private final Object mLock = new Object();
92
Keun young Parkb241d022020-04-20 20:31:34 -070093 private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread(
94 getClass().getSimpleName());
95 private final Handler mHandler = new Handler(mHandlerThread.getLooper());
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -070096 private final AtomicBoolean mIsServiceRunning = new AtomicBoolean(false);
Zhomart Mukhamejanov0462bb62021-04-06 14:46:20 -070097 private boolean mIsDumpstateDryRun = false;
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -070098
Selim Gurunc8c82f62019-04-17 14:07:30 -070099 /**
100 * Create a CarBugreportManagerService instance.
101 *
102 * @param context the context
103 */
104 public CarBugreportManagerService(Context context) {
Zhomart Mukhamejanov3740edc2021-06-15 16:41:42 -0700105 // Per https://source.android.com/setup/develop/new-device, user builds are debuggable=0
106 this(context, !Build.IS_DEBUGGABLE);
107 }
108
109 @VisibleForTesting
110 CarBugreportManagerService(Context context, boolean isUserBuild) {
Selim Gurunc8c82f62019-04-17 14:07:30 -0700111 mContext = context;
Zhomart Mukhamejanov3740edc2021-06-15 16:41:42 -0700112 mIsUserBuild = isUserBuild;
Selim Gurunc8c82f62019-04-17 14:07:30 -0700113 }
114
115 @Override
116 public void init() {
Keun young Parkb241d022020-04-20 20:31:34 -0700117 // nothing to do
Selim Gurunc8c82f62019-04-17 14:07:30 -0700118 }
119
120 @Override
121 public void release() {
Keun young Parkb241d022020-04-20 20:31:34 -0700122 // nothing to do
Selim Gurunc8c82f62019-04-17 14:07:30 -0700123 }
124
125 @Override
126 @RequiresPermission(android.Manifest.permission.DUMP)
Selim Guruna85b7e72019-06-07 11:01:42 -0700127 public void requestBugreport(ParcelFileDescriptor output, ParcelFileDescriptor extraOutput,
Zhomart Mukhamejanov0462bb62021-04-06 14:46:20 -0700128 ICarBugreportCallback callback, boolean dumpstateDryRun) {
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700129 mContext.enforceCallingOrSelfPermission(
130 android.Manifest.permission.DUMP, "requestBugreport");
131 ensureTheCallerIsSignedWithPlatformKeys();
132 ensureTheCallerIsDesignatedBugReportApp();
133 synchronized (mLock) {
134 if (mIsServiceRunning.getAndSet(true)) {
135 Slog.w(TAG, "Bugreport Service already running");
136 reportError(callback, CarBugreportManagerCallback.CAR_BUGREPORT_IN_PROGRESS);
137 return;
138 }
Zhomart Mukhamejanov0462bb62021-04-06 14:46:20 -0700139 requestBugReportLocked(output, extraOutput, callback, dumpstateDryRun);
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700140 }
141 }
Selim Gurunc4d7c012019-05-13 20:27:22 -0700142
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700143 @Override
144 @RequiresPermission(android.Manifest.permission.DUMP)
145 public void cancelBugreport() {
146 mContext.enforceCallingOrSelfPermission(
147 android.Manifest.permission.DUMP, "cancelBugreport");
148 ensureTheCallerIsSignedWithPlatformKeys();
149 ensureTheCallerIsDesignatedBugReportApp();
150 synchronized (mLock) {
151 if (!mIsServiceRunning.getAndSet(false)) {
152 Slog.i(TAG, "Failed to cancel. Service is not running.");
153 return;
154 }
155 Slog.i(TAG, "Cancelling the running bugreport");
156 mHandler.removeCallbacksAndMessages(/* token= */ null);
157 // This tells init to cancel the services. Note that this is achieved through
158 // setting a system property which is not thread-safe. So the lock here offers
159 // thread-safety only among callers of the API.
160 try {
161 SystemProperties.set("ctl.stop", BUGREPORTD_SERVICE);
162 } catch (RuntimeException e) {
163 Slog.e(TAG, "Failed to stop " + BUGREPORTD_SERVICE, e);
164 }
165 try {
166 // Stop DUMPSTATEZ_SERVICE service too, because stopping BUGREPORTD_SERVICE doesn't
167 // guarantee stopping DUMPSTATEZ_SERVICE.
168 SystemProperties.set("ctl.stop", DUMPSTATEZ_SERVICE);
169 } catch (RuntimeException e) {
170 Slog.e(TAG, "Failed to stop " + DUMPSTATEZ_SERVICE, e);
171 }
Zhomart Mukhamejanov0462bb62021-04-06 14:46:20 -0700172 if (mIsDumpstateDryRun) {
173 setDumpstateDryRun(false);
174 }
175 }
176 }
177
178 /** See {@code dumpstate} docs to learn about dry_run. */
179 private void setDumpstateDryRun(boolean dryRun) {
180 try {
181 SystemProperties.set("dumpstate.dry_run", dryRun ? "true" : null);
182 } catch (RuntimeException e) {
183 Slog.e(TAG, "Failed to set dumpstate.dry_run", e);
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700184 }
185 }
186
187 private void ensureTheCallerIsSignedWithPlatformKeys() {
Selim Gurun606b2ec2019-05-24 22:47:47 -0700188 PackageManager pm = mContext.getPackageManager();
189 int callingUid = Binder.getCallingUid();
190 if (pm.checkSignatures(Process.myUid(), callingUid) != PackageManager.SIGNATURE_MATCH) {
191 throw new SecurityException("Caller " + pm.getNameForUid(callingUid)
192 + " does not have the right signature");
193 }
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700194 }
195
196 /** Checks only on user builds. */
197 private void ensureTheCallerIsDesignatedBugReportApp() {
Zhomart Mukhamejanov3740edc2021-06-15 16:41:42 -0700198 if (!mIsUserBuild) {
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700199 return;
200 }
Zhomart Mukhamejanov30862412019-06-10 16:59:15 -0700201 String defaultAppPkgName = mContext.getString(R.string.config_car_bugreport_application);
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700202 int callingUid = Binder.getCallingUid();
203 PackageManager pm = mContext.getPackageManager();
Selim Gurun606b2ec2019-05-24 22:47:47 -0700204 String[] packageNamesForCallerUid = pm.getPackagesForUid(callingUid);
Selim Gurun606b2ec2019-05-24 22:47:47 -0700205 if (packageNamesForCallerUid != null) {
206 for (String packageName : packageNamesForCallerUid) {
207 if (defaultAppPkgName.equals(packageName)) {
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700208 return;
Selim Gurun606b2ec2019-05-24 22:47:47 -0700209 }
210 }
211 }
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700212 throw new SecurityException("Caller " + pm.getNameForUid(callingUid)
213 + " is not a designated bugreport app");
Selim Gurunc4d7c012019-05-13 20:27:22 -0700214 }
215
216 @GuardedBy("mLock")
Zhomart Mukhamejanov0462bb62021-04-06 14:46:20 -0700217 private void requestBugReportLocked(
218 ParcelFileDescriptor output,
219 ParcelFileDescriptor extraOutput,
220 ICarBugreportCallback callback,
221 boolean dumpstateDryRun) {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700222 Slog.i(TAG, "Starting " + BUGREPORTD_SERVICE);
Zhomart Mukhamejanov0462bb62021-04-06 14:46:20 -0700223 mIsDumpstateDryRun = dumpstateDryRun;
224 if (mIsDumpstateDryRun) {
225 setDumpstateDryRun(true);
226 }
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700227 try {
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700228 // This tells init to start the service. Note that this is achieved through
229 // setting a system property which is not thread-safe. So the lock here offers
230 // thread-safety only among callers of the API.
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700231 SystemProperties.set("ctl.start", BUGREPORTD_SERVICE);
232 } catch (RuntimeException e) {
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700233 mIsServiceRunning.set(false);
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700234 Slog.e(TAG, "Failed to start " + BUGREPORTD_SERVICE, e);
235 reportError(callback, CAR_BUGREPORT_DUMPSTATE_FAILED);
236 return;
237 }
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700238 mHandler.post(() -> {
239 try {
240 processBugreportSockets(output, extraOutput, callback);
241 } finally {
Zhomart Mukhamejanov0462bb62021-04-06 14:46:20 -0700242 if (mIsDumpstateDryRun) {
243 setDumpstateDryRun(false);
244 }
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700245 mIsServiceRunning.set(false);
246 }
247 });
Selim Gurunc4d7c012019-05-13 20:27:22 -0700248 }
249
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700250 private void handleProgress(String line, ICarBugreportCallback callback) {
251 String progressOverTotal = line.substring(PROGRESS_PREFIX.length());
252 String[] parts = progressOverTotal.split("/");
253 if (parts.length != 2) {
254 Slog.w(TAG, "Invalid progress line from bugreportz: " + line);
255 return;
256 }
257 float progress;
258 float total;
Selim Gurunc4d7c012019-05-13 20:27:22 -0700259 try {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700260 progress = Float.parseFloat(parts[0]);
261 total = Float.parseFloat(parts[1]);
262 } catch (NumberFormatException e) {
263 Slog.w(TAG, "Invalid progress value: " + line, e);
Selim Gurunc4d7c012019-05-13 20:27:22 -0700264 return;
265 }
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700266 if (total == 0) {
267 Slog.w(TAG, "Invalid progress total value: " + line);
Selim Gurunc4d7c012019-05-13 20:27:22 -0700268 return;
269 }
Selim Gurunc4d7c012019-05-13 20:27:22 -0700270 try {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700271 callback.onProgress(100f * progress / total);
Selim Gurunc4d7c012019-05-13 20:27:22 -0700272 } catch (RemoteException e) {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700273 Slog.e(TAG, "Failed to call onProgress callback", e);
Selim Gurunc4d7c012019-05-13 20:27:22 -0700274 }
275 }
276
Selim Guruna85b7e72019-06-07 11:01:42 -0700277 private void handleFinished(ParcelFileDescriptor output, ParcelFileDescriptor extraOutput,
278 ICarBugreportCallback callback) {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700279 Slog.i(TAG, "Finished reading bugreport");
Selim Guruna85b7e72019-06-07 11:01:42 -0700280 // copysockettopfd calls callback.onError on error
281 if (!copySocketToPfd(output, BUGREPORT_OUTPUT_SOCKET, callback)) {
282 return;
283 }
284 if (!copySocketToPfd(extraOutput, BUGREPORT_EXTRA_OUTPUT_SOCKET, callback)) {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700285 return;
286 }
Selim Gurunc4d7c012019-05-13 20:27:22 -0700287 try {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700288 callback.onFinished();
289 } catch (RemoteException e) {
290 Slog.e(TAG, "Failed to call onFinished callback", e);
291 }
292 }
293
294 /**
295 * Reads from dumpstate progress and output sockets and invokes appropriate callbacks.
296 *
297 * <p>dumpstate prints {@code BEGIN:} right away, then prints {@code PROGRESS:} as it
298 * progresses. When it finishes or fails it prints {@code OK:pathToTheZipFile} or
299 * {@code FAIL:message} accordingly.
300 */
301 private void processBugreportSockets(
Selim Guruna85b7e72019-06-07 11:01:42 -0700302 ParcelFileDescriptor output, ParcelFileDescriptor extraOutput,
303 ICarBugreportCallback callback) {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700304 LocalSocket localSocket = connectSocket(BUGREPORT_PROGRESS_SOCKET);
305 if (localSocket == null) {
306 reportError(callback, CAR_BUGREPORT_DUMPSTATE_CONNECTION_FAILED);
307 return;
308 }
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700309 try (BufferedReader reader =
310 new BufferedReader(new InputStreamReader(localSocket.getInputStream()))) {
311 String line;
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700312 while (mIsServiceRunning.get() && (line = reader.readLine()) != null) {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700313 if (line.startsWith(PROGRESS_PREFIX)) {
314 handleProgress(line, callback);
315 } else if (line.startsWith(FAIL_PREFIX)) {
316 String errorMessage = line.substring(FAIL_PREFIX.length());
317 Slog.e(TAG, "Failed to dumpstate: " + errorMessage);
318 reportError(callback, CAR_BUGREPORT_DUMPSTATE_FAILED);
319 return;
320 } else if (line.startsWith(OK_PREFIX)) {
Selim Guruna85b7e72019-06-07 11:01:42 -0700321 handleFinished(output, extraOutput, callback);
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700322 return;
323 } else if (!line.startsWith(BEGIN_PREFIX)) {
324 Slog.w(TAG, "Received unknown progress line from dumpstate: " + line);
325 }
326 }
327 Slog.e(TAG, "dumpstate progress unexpectedly ended");
328 reportError(callback, CAR_BUGREPORT_DUMPSTATE_FAILED);
329 } catch (IOException | RuntimeException e) {
330 Slog.i(TAG, "Failed to read from progress socket", e);
331 reportError(callback, CAR_BUGREPORT_DUMPSTATE_CONNECTION_FAILED);
332 }
333 }
334
335 private boolean copySocketToPfd(
Selim Guruna85b7e72019-06-07 11:01:42 -0700336 ParcelFileDescriptor pfd, String remoteSocket, ICarBugreportCallback callback) {
337 LocalSocket localSocket = connectSocket(remoteSocket);
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700338 if (localSocket == null) {
339 reportError(callback, CAR_BUGREPORT_DUMPSTATE_CONNECTION_FAILED);
Selim Gurunc4d7c012019-05-13 20:27:22 -0700340 return false;
341 }
342
343 try (
344 DataInputStream in = new DataInputStream(localSocket.getInputStream());
345 DataOutputStream out =
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700346 new DataOutputStream(new ParcelFileDescriptor.AutoCloseOutputStream(pfd))
Selim Gurunc4d7c012019-05-13 20:27:22 -0700347 ) {
348 rawCopyStream(out, in);
349 } catch (IOException | RuntimeException e) {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700350 Slog.e(TAG, "Failed to grab dump state from " + BUGREPORT_OUTPUT_SOCKET, e);
351 reportError(callback, CAR_BUGREPORT_DUMPSTATE_FAILED);
Selim Gurunc4d7c012019-05-13 20:27:22 -0700352 return false;
353 }
354 return true;
355 }
356
Selim Gurunc8c82f62019-04-17 14:07:30 -0700357 private void reportError(ICarBugreportCallback callback, int errorCode) {
358 try {
359 callback.onError(errorCode);
360 } catch (RemoteException e) {
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700361 Slog.e(TAG, "onError() failed", e);
Selim Gurunc8c82f62019-04-17 14:07:30 -0700362 }
363 }
364
Selim Gurunc4d7c012019-05-13 20:27:22 -0700365 @Override
Felipe Leme176a5fd2021-01-20 15:48:33 -0800366 public void dump(IndentingPrintWriter writer) {
Selim Gurunc4d7c012019-05-13 20:27:22 -0700367 // TODO(sgurun) implement
368 }
369
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700370 @Nullable
371 private LocalSocket connectSocket(@NonNull String socketName) {
Selim Gurunc8c82f62019-04-17 14:07:30 -0700372 LocalSocket socket = new LocalSocket();
373 // The dumpstate socket will be created by init upon receiving the
374 // service request. It may not be ready by this point. So we will
375 // keep retrying until success or reaching timeout.
376 int retryCount = 0;
377 while (true) {
Selim Guruna85b7e72019-06-07 11:01:42 -0700378 // There are a few factors impacting the socket delay:
379 // 1. potential system slowness
Zhomart Mukhamejanov6180b8c2021-03-04 12:03:46 -0800380 // 2. carbugreportd takes the screenshots early (before starting dumpstate). This
Selim Guruna85b7e72019-06-07 11:01:42 -0700381 // should be taken into account as the socket opens after screenshots are
382 // captured.
383 // Therefore we are generous in setting the timeout. Most cases should not even
384 // come close to the timeouts, but since bugreports are taken when there is a
385 // system issue, it is hard to guess.
Zhomart Mukhamejanov68348632020-05-11 19:33:50 -0700386 // The following lines waits for SOCKET_CONNECTION_RETRY_DELAY_IN_MS or until
387 // mIsServiceRunning becomes false.
388 for (int i = 0; i < SOCKET_CONNECTION_RETRY_DELAY_IN_MS / 50; i++) {
389 if (!mIsServiceRunning.get()) {
390 Slog.i(TAG, "Failed to connect to socket " + socketName
391 + ". The service is prematurely cancelled.");
392 return null;
393 }
394 SystemClock.sleep(50); // Millis.
395 }
396
Selim Gurunc8c82f62019-04-17 14:07:30 -0700397 try {
Selim Gurunc4d7c012019-05-13 20:27:22 -0700398 socket.connect(new LocalSocketAddress(socketName,
Selim Gurunc8c82f62019-04-17 14:07:30 -0700399 LocalSocketAddress.Namespace.RESERVED));
400 return socket;
401 } catch (IOException e) {
Selim Gurunc8c82f62019-04-17 14:07:30 -0700402 if (++retryCount >= SOCKET_CONNECTION_MAX_RETRY) {
Zhomart Mukhamejanovad23f272019-06-04 20:27:24 -0700403 Slog.i(TAG, "Failed to connect to dumpstate socket " + socketName
404 + " after " + retryCount + " retries", e);
405 return null;
Selim Gurunc8c82f62019-04-17 14:07:30 -0700406 }
Eric Jeongbd5fb562020-12-21 13:49:40 -0800407 Slog.i(TAG, "Failed to connect to " + socketName + ". Will try again. "
Selim Gurunc4d7c012019-05-13 20:27:22 -0700408 + e.getMessage());
Selim Gurunc8c82f62019-04-17 14:07:30 -0700409 }
410 }
Selim Gurunc8c82f62019-04-17 14:07:30 -0700411 }
412
413 // does not close the reader or writer.
414 private static void rawCopyStream(OutputStream writer, InputStream reader) throws IOException {
415 int read;
416 byte[] buf = new byte[8192];
417 while ((read = reader.read(buf, 0, buf.length)) > 0) {
418 writer.write(buf, 0, read);
419 }
420 }
421}