blob: 77059d918052be436371ca63a08fd7299eea9787 [file] [log] [blame]
Zimuzo0de99f32019-03-18 13:23:55 +00001/*
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.server;
18import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE;
19import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES;
20import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES;
Zimuzo1a9aac72019-04-24 14:07:24 +010021import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
Zimuzo0de99f32019-03-18 13:23:55 +000022
23import android.Manifest;
24import android.annotation.MainThread;
25import android.annotation.Nullable;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.ServiceConnection;
30import android.content.pm.PackageManager;
31import android.content.pm.ResolveInfo;
32import android.content.pm.ServiceInfo;
33import android.os.IBinder;
34import android.os.RemoteCallback;
35import android.os.RemoteException;
36import android.os.UserHandle;
37import android.service.watchdog.ExplicitHealthCheckService;
38import android.service.watchdog.IExplicitHealthCheckService;
39import android.text.TextUtils;
Zimuzo6997f3c2019-04-05 17:06:25 +010040import android.util.ArraySet;
Zimuzo0de99f32019-03-18 13:23:55 +000041import android.util.Slog;
42
43import com.android.internal.annotations.GuardedBy;
Zimuzocaa435e2019-03-20 11:16:06 +000044import com.android.internal.util.Preconditions;
Zimuzo0de99f32019-03-18 13:23:55 +000045
Zimuzocb148b22019-04-01 18:54:17 +010046import java.util.Collection;
Zimuzocaa435e2019-03-20 11:16:06 +000047import java.util.Collections;
Zimuzocb148b22019-04-01 18:54:17 +010048import java.util.Iterator;
Zimuzo0de99f32019-03-18 13:23:55 +000049import java.util.List;
Daulet Zhanguzinea1a7ca2020-01-03 09:46:50 +000050import java.util.Objects;
Zimuzocb148b22019-04-01 18:54:17 +010051import java.util.Set;
Zimuzo0de99f32019-03-18 13:23:55 +000052import java.util.function.Consumer;
53
Zimuzocb148b22019-04-01 18:54:17 +010054// TODO(b/120598832): Add tests
Zimuzo0de99f32019-03-18 13:23:55 +000055/**
56 * Controls the connections with {@link ExplicitHealthCheckService}.
57 */
58class ExplicitHealthCheckController {
59 private static final String TAG = "ExplicitHealthCheckController";
60 private final Object mLock = new Object();
61 private final Context mContext;
Zimuzocaa435e2019-03-20 11:16:06 +000062
Zimuzocaa435e2019-03-20 11:16:06 +000063 // Called everytime a package passes the health check, so the watchdog is notified of the
64 // passing check. In practice, should never be null after it has been #setEnabled.
Zimuzocb148b22019-04-01 18:54:17 +010065 // To prevent deadlocks between the controller and watchdog threads, we have
66 // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class.
67 // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer.
Zimuzocaa435e2019-03-20 11:16:06 +000068 @GuardedBy("mLock") @Nullable private Consumer<String> mPassedConsumer;
Zimuzocb148b22019-04-01 18:54:17 +010069 // Called everytime after a successful #syncRequest call, so the watchdog can receive packages
70 // supporting health checks and update its internal state. In practice, should never be null
71 // after it has been #setEnabled.
72 // To prevent deadlocks between the controller and watchdog threads, we have
73 // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class.
74 // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer.
Zimuzo1a9aac72019-04-24 14:07:24 +010075 @GuardedBy("mLock") @Nullable private Consumer<List<PackageConfig>> mSupportedConsumer;
Zimuzocb148b22019-04-01 18:54:17 +010076 // Called everytime we need to notify the watchdog to sync requests between itself and the
77 // health check service. In practice, should never be null after it has been #setEnabled.
78 // To prevent deadlocks between the controller and watchdog threads, we have
79 // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class.
80 // It's easier to just NOT hold #mLock when calling into watchdog code on this runnable.
81 @GuardedBy("mLock") @Nullable private Runnable mNotifySyncRunnable;
Zimuzocaa435e2019-03-20 11:16:06 +000082 // Actual binder object to the explicit health check service.
Zimuzo0de99f32019-03-18 13:23:55 +000083 @GuardedBy("mLock") @Nullable private IExplicitHealthCheckService mRemoteService;
Zimuzocb148b22019-04-01 18:54:17 +010084 // Connection to the explicit health check service, necessary to unbind.
85 // We should only try to bind if mConnection is null, non-null indicates we
86 // are connected or at least connecting.
Zimuzocaa435e2019-03-20 11:16:06 +000087 @GuardedBy("mLock") @Nullable private ServiceConnection mConnection;
88 // Bind state of the explicit health check service.
89 @GuardedBy("mLock") private boolean mEnabled;
Zimuzo0de99f32019-03-18 13:23:55 +000090
91 ExplicitHealthCheckController(Context context) {
92 mContext = context;
93 }
94
Zimuzocb148b22019-04-01 18:54:17 +010095 /** Enables or disables explicit health checks. */
96 public void setEnabled(boolean enabled) {
97 synchronized (mLock) {
98 Slog.i(TAG, "Explicit health checks " + (enabled ? "enabled." : "disabled."));
99 mEnabled = enabled;
100 }
101 }
102
103 /**
104 * Sets callbacks to listen to important events from the controller.
105 *
106 * <p> Should be called once at initialization before any other calls to the controller to
107 * ensure a happens-before relationship of the set parameters and visibility on other threads.
108 */
109 public void setCallbacks(Consumer<String> passedConsumer,
Zimuzo1a9aac72019-04-24 14:07:24 +0100110 Consumer<List<PackageConfig>> supportedConsumer, Runnable notifySyncRunnable) {
Zimuzocb148b22019-04-01 18:54:17 +0100111 synchronized (mLock) {
112 if (mPassedConsumer != null || mSupportedConsumer != null
113 || mNotifySyncRunnable != null) {
114 Slog.wtf(TAG, "Resetting health check controller callbacks");
115 }
116
Daulet Zhanguzinea1a7ca2020-01-03 09:46:50 +0000117 mPassedConsumer = Objects.requireNonNull(passedConsumer);
118 mSupportedConsumer = Objects.requireNonNull(supportedConsumer);
119 mNotifySyncRunnable = Objects.requireNonNull(notifySyncRunnable);
Zimuzocb148b22019-04-01 18:54:17 +0100120 }
121 }
122
123 /**
124 * Calls the health check service to request or cancel packages based on
125 * {@code newRequestedPackages}.
126 *
127 * <p> Supported packages in {@code newRequestedPackages} that have not been previously
128 * requested will be requested while supported packages not in {@code newRequestedPackages}
129 * but were previously requested will be cancelled.
130 *
131 * <p> This handles binding and unbinding to the health check service as required.
132 *
133 * <p> Note, calling this may modify {@code newRequestedPackages}.
134 *
135 * <p> Note, this method is not thread safe, all calls should be serialized.
136 */
137 public void syncRequests(Set<String> newRequestedPackages) {
138 boolean enabled;
139 synchronized (mLock) {
140 enabled = mEnabled;
141 }
142
143 if (!enabled) {
144 Slog.i(TAG, "Health checks disabled, no supported packages");
145 // Call outside lock
146 mSupportedConsumer.accept(Collections.emptyList());
147 return;
148 }
149
Zimuzo1a9aac72019-04-24 14:07:24 +0100150 getSupportedPackages(supportedPackageConfigs -> {
Zimuzocb148b22019-04-01 18:54:17 +0100151 // Notify the watchdog without lock held
Zimuzo1a9aac72019-04-24 14:07:24 +0100152 mSupportedConsumer.accept(supportedPackageConfigs);
Zimuzocb148b22019-04-01 18:54:17 +0100153 getRequestedPackages(previousRequestedPackages -> {
154 synchronized (mLock) {
155 // Hold lock so requests and cancellations are sent atomically.
156 // It is important we don't mix requests from multiple threads.
157
Zimuzo6997f3c2019-04-05 17:06:25 +0100158 Set<String> supportedPackages = new ArraySet<>();
Zimuzo1a9aac72019-04-24 14:07:24 +0100159 for (PackageConfig config : supportedPackageConfigs) {
160 supportedPackages.add(config.getPackageName());
Zimuzo6997f3c2019-04-05 17:06:25 +0100161 }
Zimuzocb148b22019-04-01 18:54:17 +0100162 // Note, this may modify newRequestedPackages
163 newRequestedPackages.retainAll(supportedPackages);
164
165 // Cancel packages no longer requested
166 actOnDifference(previousRequestedPackages,
167 newRequestedPackages, p -> cancel(p));
168 // Request packages not yet requested
169 actOnDifference(newRequestedPackages,
170 previousRequestedPackages, p -> request(p));
171
172 if (newRequestedPackages.isEmpty()) {
173 Slog.i(TAG, "No more health check requests, unbinding...");
174 unbindService();
175 return;
176 }
177 }
178 });
179 });
180 }
181
182 private void actOnDifference(Collection<String> collection1, Collection<String> collection2,
183 Consumer<String> action) {
184 Iterator<String> iterator = collection1.iterator();
185 while (iterator.hasNext()) {
186 String packageName = iterator.next();
187 if (!collection2.contains(packageName)) {
188 action.accept(packageName);
189 }
190 }
191 }
192
Zimuzo0de99f32019-03-18 13:23:55 +0000193 /**
194 * Requests an explicit health check for {@code packageName}.
Zimuzocaa435e2019-03-20 11:16:06 +0000195 * After this request, the callback registered on {@link #setCallbacks} can receive explicit
Zimuzo0de99f32019-03-18 13:23:55 +0000196 * health check passed results.
Zimuzo0de99f32019-03-18 13:23:55 +0000197 */
Zimuzocb148b22019-04-01 18:54:17 +0100198 private void request(String packageName) {
Zimuzo0de99f32019-03-18 13:23:55 +0000199 synchronized (mLock) {
Zimuzocb148b22019-04-01 18:54:17 +0100200 if (!prepareServiceLocked("request health check for " + packageName)) {
Zimuzocaa435e2019-03-20 11:16:06 +0000201 return;
202 }
203
Zimuzocaa435e2019-03-20 11:16:06 +0000204 Slog.i(TAG, "Requesting health check for package " + packageName);
Zimuzocb148b22019-04-01 18:54:17 +0100205 try {
206 mRemoteService.request(packageName);
207 } catch (RemoteException e) {
208 Slog.w(TAG, "Failed to request health check for package " + packageName, e);
209 }
Zimuzo0de99f32019-03-18 13:23:55 +0000210 }
211 }
212
213 /**
214 * Cancels all explicit health checks for {@code packageName}.
Zimuzocaa435e2019-03-20 11:16:06 +0000215 * After this request, the callback registered on {@link #setCallbacks} can no longer receive
Zimuzo0de99f32019-03-18 13:23:55 +0000216 * explicit health check passed results.
Zimuzo0de99f32019-03-18 13:23:55 +0000217 */
Zimuzocb148b22019-04-01 18:54:17 +0100218 private void cancel(String packageName) {
Zimuzo0de99f32019-03-18 13:23:55 +0000219 synchronized (mLock) {
Zimuzocb148b22019-04-01 18:54:17 +0100220 if (!prepareServiceLocked("cancel health check for " + packageName)) {
Zimuzocaa435e2019-03-20 11:16:06 +0000221 return;
222 }
223
Zimuzocaa435e2019-03-20 11:16:06 +0000224 Slog.i(TAG, "Cancelling health check for package " + packageName);
Zimuzocb148b22019-04-01 18:54:17 +0100225 try {
226 mRemoteService.cancel(packageName);
227 } catch (RemoteException e) {
228 // Do nothing, if the service is down, when it comes up, we will sync requests,
229 // if there's some other error, retrying wouldn't fix anyways.
230 Slog.w(TAG, "Failed to cancel health check for package " + packageName, e);
231 }
Zimuzo0de99f32019-03-18 13:23:55 +0000232 }
233 }
234
235 /**
236 * Returns the packages that we can request explicit health checks for.
237 * The packages will be returned to the {@code consumer}.
Zimuzo0de99f32019-03-18 13:23:55 +0000238 */
Zimuzo1a9aac72019-04-24 14:07:24 +0100239 private void getSupportedPackages(Consumer<List<PackageConfig>> consumer) {
Zimuzo0de99f32019-03-18 13:23:55 +0000240 synchronized (mLock) {
Zimuzocb148b22019-04-01 18:54:17 +0100241 if (!prepareServiceLocked("get health check supported packages")) {
Zimuzocaa435e2019-03-20 11:16:06 +0000242 return;
243 }
244
Zimuzocb148b22019-04-01 18:54:17 +0100245 Slog.d(TAG, "Getting health check supported packages");
246 try {
Zimuzo0de99f32019-03-18 13:23:55 +0000247 mRemoteService.getSupportedPackages(new RemoteCallback(result -> {
Zimuzo1a9aac72019-04-24 14:07:24 +0100248 List<PackageConfig> packages =
Zimuzo6997f3c2019-04-05 17:06:25 +0100249 result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES);
Zimuzocb148b22019-04-01 18:54:17 +0100250 Slog.i(TAG, "Explicit health check supported packages " + packages);
251 consumer.accept(packages);
Zimuzo0de99f32019-03-18 13:23:55 +0000252 }));
Zimuzocb148b22019-04-01 18:54:17 +0100253 } catch (RemoteException e) {
254 // Request failed, treat as if all observed packages are supported, if any packages
255 // expire during this period, we may incorrectly treat it as failing health checks
256 // even if we don't support health checks for the package.
257 Slog.w(TAG, "Failed to get health check supported packages", e);
Zimuzo0de99f32019-03-18 13:23:55 +0000258 }
259 }
260 }
261
262 /**
263 * Returns the packages for which health checks are currently in progress.
264 * The packages will be returned to the {@code consumer}.
Zimuzo0de99f32019-03-18 13:23:55 +0000265 */
Zimuzocb148b22019-04-01 18:54:17 +0100266 private void getRequestedPackages(Consumer<List<String>> consumer) {
Zimuzo0de99f32019-03-18 13:23:55 +0000267 synchronized (mLock) {
Zimuzocb148b22019-04-01 18:54:17 +0100268 if (!prepareServiceLocked("get health check requested packages")) {
Zimuzocaa435e2019-03-20 11:16:06 +0000269 return;
270 }
271
Zimuzocaa435e2019-03-20 11:16:06 +0000272 Slog.d(TAG, "Getting health check requested packages");
Zimuzocb148b22019-04-01 18:54:17 +0100273 try {
274 mRemoteService.getRequestedPackages(new RemoteCallback(result -> {
275 List<String> packages = result.getStringArrayList(EXTRA_REQUESTED_PACKAGES);
276 Slog.i(TAG, "Explicit health check requested packages " + packages);
277 consumer.accept(packages);
278 }));
279 } catch (RemoteException e) {
280 // Request failed, treat as if we haven't requested any packages, if any packages
281 // were actually requested, they will not be cancelled now. May be cancelled later
282 Slog.w(TAG, "Failed to get health check requested packages", e);
Zimuzocaa435e2019-03-20 11:16:06 +0000283 }
284 }
285 }
286
Zimuzo0de99f32019-03-18 13:23:55 +0000287 /**
Zimuzocb148b22019-04-01 18:54:17 +0100288 * Binds to the explicit health check service if the controller is enabled and
289 * not already bound.
Zimuzo0de99f32019-03-18 13:23:55 +0000290 */
Zimuzocaa435e2019-03-20 11:16:06 +0000291 private void bindService() {
Zimuzo0de99f32019-03-18 13:23:55 +0000292 synchronized (mLock) {
Zimuzocb148b22019-04-01 18:54:17 +0100293 if (!mEnabled || mConnection != null || mRemoteService != null) {
294 if (!mEnabled) {
295 Slog.i(TAG, "Not binding to service, service disabled");
296 } else if (mRemoteService != null) {
297 Slog.i(TAG, "Not binding to service, service already connected");
298 } else {
299 Slog.i(TAG, "Not binding to service, service already connecting");
300 }
Zimuzocaa435e2019-03-20 11:16:06 +0000301 return;
Zimuzo0de99f32019-03-18 13:23:55 +0000302 }
Zimuzocaa435e2019-03-20 11:16:06 +0000303 ComponentName component = getServiceComponentNameLocked();
304 if (component == null) {
305 Slog.wtf(TAG, "Explicit health check service not found");
306 return;
307 }
308
309 Intent intent = new Intent();
310 intent.setComponent(component);
Zimuzo0de99f32019-03-18 13:23:55 +0000311 mConnection = new ServiceConnection() {
312 @Override
313 public void onServiceConnected(ComponentName name, IBinder service) {
Zimuzocaa435e2019-03-20 11:16:06 +0000314 Slog.i(TAG, "Explicit health check service is connected " + name);
Zimuzocb148b22019-04-01 18:54:17 +0100315 initState(service);
Zimuzo0de99f32019-03-18 13:23:55 +0000316 }
317
318 @Override
319 @MainThread
320 public void onServiceDisconnected(ComponentName name) {
Zimuzocaa435e2019-03-20 11:16:06 +0000321 // Service crashed or process was killed, #onServiceConnected will be called.
322 // Don't need to re-bind.
Zimuzo0de99f32019-03-18 13:23:55 +0000323 Slog.i(TAG, "Explicit health check service is disconnected " + name);
Zimuzocb148b22019-04-01 18:54:17 +0100324 synchronized (mLock) {
325 mRemoteService = null;
326 }
Zimuzo0de99f32019-03-18 13:23:55 +0000327 }
328
329 @Override
330 public void onBindingDied(ComponentName name) {
Zimuzocaa435e2019-03-20 11:16:06 +0000331 // Application hosting service probably got updated
332 // Need to re-bind.
Zimuzocb148b22019-04-01 18:54:17 +0100333 Slog.i(TAG, "Explicit health check service binding is dead. Rebind: " + name);
334 unbindService();
335 bindService();
Zimuzo0de99f32019-03-18 13:23:55 +0000336 }
337
338 @Override
339 public void onNullBinding(ComponentName name) {
Zimuzocaa435e2019-03-20 11:16:06 +0000340 // Should never happen. Service returned null from #onBind.
341 Slog.wtf(TAG, "Explicit health check service binding is null?? " + name);
Zimuzo0de99f32019-03-18 13:23:55 +0000342 }
343 };
344
Zimuzocb148b22019-04-01 18:54:17 +0100345 mContext.bindServiceAsUser(intent, mConnection,
346 Context.BIND_AUTO_CREATE, UserHandle.of(UserHandle.USER_SYSTEM));
347 Slog.i(TAG, "Explicit health check service is bound");
Zimuzo0de99f32019-03-18 13:23:55 +0000348 }
349 }
350
Zimuzocaa435e2019-03-20 11:16:06 +0000351 /** Unbinds the explicit health check service. */
352 private void unbindService() {
Zimuzo0de99f32019-03-18 13:23:55 +0000353 synchronized (mLock) {
354 if (mRemoteService != null) {
355 mContext.unbindService(mConnection);
Zimuzocaa435e2019-03-20 11:16:06 +0000356 mRemoteService = null;
Zimuzocb148b22019-04-01 18:54:17 +0100357 mConnection = null;
Zimuzo0de99f32019-03-18 13:23:55 +0000358 }
Zimuzocb148b22019-04-01 18:54:17 +0100359 Slog.i(TAG, "Explicit health check service is unbound");
Zimuzo0de99f32019-03-18 13:23:55 +0000360 }
361 }
362
363 @GuardedBy("mLock")
364 @Nullable
365 private ServiceInfo getServiceInfoLocked() {
366 final String packageName =
367 mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
368 if (packageName == null) {
369 Slog.w(TAG, "no external services package!");
370 return null;
371 }
372
373 final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE);
374 intent.setPackage(packageName);
375 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
376 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
377 if (resolveInfo == null || resolveInfo.serviceInfo == null) {
378 Slog.w(TAG, "No valid components found.");
379 return null;
380 }
381 return resolveInfo.serviceInfo;
382 }
383
384 @GuardedBy("mLock")
385 @Nullable
386 private ComponentName getServiceComponentNameLocked() {
387 final ServiceInfo serviceInfo = getServiceInfoLocked();
388 if (serviceInfo == null) {
389 return null;
390 }
391
392 final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
393 if (!Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE
394 .equals(serviceInfo.permission)) {
395 Slog.w(TAG, name.flattenToShortString() + " does not require permission "
396 + Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE);
397 return null;
398 }
399 return name;
400 }
401
Zimuzocaa435e2019-03-20 11:16:06 +0000402 private void initState(IBinder service) {
Zimuzo0de99f32019-03-18 13:23:55 +0000403 synchronized (mLock) {
Zimuzocb148b22019-04-01 18:54:17 +0100404 if (!mEnabled) {
405 Slog.w(TAG, "Attempting to connect disabled service?? Unbinding...");
406 // Very unlikely, but we disabled the service after binding but before we connected
407 unbindService();
408 return;
409 }
Zimuzocaa435e2019-03-20 11:16:06 +0000410 mRemoteService = IExplicitHealthCheckService.Stub.asInterface(service);
411 try {
412 mRemoteService.setCallback(new RemoteCallback(result -> {
413 String packageName = result.getString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE);
414 if (!TextUtils.isEmpty(packageName)) {
Zimuzocb148b22019-04-01 18:54:17 +0100415 if (mPassedConsumer == null) {
416 Slog.wtf(TAG, "Health check passed for package " + packageName
417 + "but no consumer registered.");
418 } else {
419 // Call without lock held
420 mPassedConsumer.accept(packageName);
Zimuzocaa435e2019-03-20 11:16:06 +0000421 }
422 } else {
Zimuzocb148b22019-04-01 18:54:17 +0100423 Slog.wtf(TAG, "Empty package passed explicit health check?");
Zimuzocaa435e2019-03-20 11:16:06 +0000424 }
425 }));
Zimuzocb148b22019-04-01 18:54:17 +0100426 Slog.i(TAG, "Service initialized, syncing requests");
Zimuzocaa435e2019-03-20 11:16:06 +0000427 } catch (RemoteException e) {
428 Slog.wtf(TAG, "Could not setCallback on explicit health check service");
429 }
Zimuzo0de99f32019-03-18 13:23:55 +0000430 }
Zimuzocb148b22019-04-01 18:54:17 +0100431 // Calling outside lock
432 mNotifySyncRunnable.run();
Zimuzo0de99f32019-03-18 13:23:55 +0000433 }
434
Zimuzocb148b22019-04-01 18:54:17 +0100435 /**
436 * Prepares the health check service to receive requests.
437 *
438 * @return {@code true} if it is ready and we can proceed with a request,
439 * {@code false} otherwise. If it is not ready, and the service is enabled,
440 * we will bind and the request should be automatically attempted later.
441 */
Zimuzo0de99f32019-03-18 13:23:55 +0000442 @GuardedBy("mLock")
Zimuzocb148b22019-04-01 18:54:17 +0100443 private boolean prepareServiceLocked(String action) {
444 if (mRemoteService != null && mEnabled) {
445 return true;
Zimuzo0de99f32019-03-18 13:23:55 +0000446 }
Zimuzocb148b22019-04-01 18:54:17 +0100447 Slog.i(TAG, "Service not ready to " + action
448 + (mEnabled ? ". Binding..." : ". Disabled"));
449 if (mEnabled) {
450 bindService();
451 }
452 return false;
Zimuzo0de99f32019-03-18 13:23:55 +0000453 }
454}