blob: 7f51aa9068fca7f62daba3c271a142e709acb1bd [file] [log] [blame]
Nick Pelly6fa9ad42012-07-16 12:18:23 -07001/*
2 * Copyright (C) 2012 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;
18
Jeff Sharkey58482c552016-02-08 17:49:17 -070019import android.annotation.Nullable;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070020import android.app.ActivityManager;
Victoria Lease03cdd3d2013-02-01 15:15:54 -080021import android.content.BroadcastReceiver;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070022import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
Victoria Lease03cdd3d2013-02-01 15:15:54 -080025import android.content.IntentFilter;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070026import android.content.ServiceConnection;
27import android.content.pm.PackageInfo;
28import android.content.pm.PackageManager;
29import android.content.pm.PackageManager.NameNotFoundException;
30import android.content.pm.ResolveInfo;
31import android.content.pm.Signature;
Zhentao Sunc5fc9982013-04-17 17:47:53 -070032import android.content.res.Resources;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070033import android.os.Bundle;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070034import android.os.Handler;
35import android.os.IBinder;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070036import android.os.Looper;
Soonil Nagarkar192710b2019-01-24 17:42:49 -080037import android.os.RemoteException;
Victoria Leaseb711d572012-10-02 13:14:11 -070038import android.os.UserHandle;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070039import android.util.Log;
Jeff Sharkey58482c552016-02-08 17:49:17 -070040import android.util.Slog;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070041
42import com.android.internal.content.PackageMonitor;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070043import com.android.internal.util.Preconditions;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070044
45import java.util.ArrayList;
46import java.util.Arrays;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070047import java.util.Collections;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070048import java.util.HashSet;
49import java.util.List;
Jeff Sharkey58482c552016-02-08 17:49:17 -070050import java.util.Objects;
Soonil Nagarkar192710b2019-01-24 17:42:49 -080051import java.util.concurrent.Callable;
52import java.util.concurrent.ExecutionException;
53import java.util.concurrent.FutureTask;
Soonil Nagarkar93ff5b82019-10-29 13:34:26 -070054import java.util.concurrent.TimeUnit;
55import java.util.concurrent.TimeoutException;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070056
57/**
58 * Find the best Service, and bind to it.
59 * Handles run-time package changes.
60 */
61public class ServiceWatcher implements ServiceConnection {
Soonil Nagarkare731ca82018-11-02 13:55:51 -070062
63 private static final String TAG = "ServiceWatcher";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070064 private static final boolean D = false;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070065
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050066 public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
Victoria Lease03cdd3d2013-02-01 15:15:54 -080067 public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070068
Soonil Nagarkar93ff5b82019-10-29 13:34:26 -070069 private static final long BLOCKING_BINDER_TIMEOUT_MS = 30 * 1000;
Soonil Nagarkar192710b2019-01-24 17:42:49 -080070
71 /** Function to run on binder interface. */
Soonil Nagarkare731ca82018-11-02 13:55:51 -070072 public interface BinderRunner {
Soonil Nagarkar192710b2019-01-24 17:42:49 -080073 /** Called to run client code with the binder. */
74 void run(IBinder binder) throws RemoteException;
75 }
76
77 /**
78 * Function to run on binder interface.
79 * @param <T> Type to return.
80 */
81 public interface BlockingBinderRunner<T> {
82 /** Called to run client code with the binder. */
83 T run(IBinder binder) throws RemoteException;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070084 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -070085
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050086 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
Soonil Nagarkare731ca82018-11-02 13:55:51 -070087 String... packageNames) {
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050088 PackageManager pm = context.getPackageManager();
Soonil Nagarkare731ca82018-11-02 13:55:51 -070089
90 ArrayList<HashSet<Signature>> signatureSets = new ArrayList<>(packageNames.length);
91 for (String packageName : packageNames) {
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050092 try {
Soonil Nagarkare731ca82018-11-02 13:55:51 -070093 Signature[] signatures = pm.getPackageInfo(packageName,
94 PackageManager.MATCH_SYSTEM_ONLY
95 | PackageManager.GET_SIGNATURES).signatures;
96
97 HashSet<Signature> set = new HashSet<>();
98 Collections.addAll(set, signatures);
99 signatureSets.add(set);
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500100 } catch (NameNotFoundException e) {
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700101 Log.w(TAG, packageName + " not found");
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500102 }
103 }
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700104 return signatureSets;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500105 }
106
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700107 /** Checks if signatures match. */
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500108 public static boolean isSignatureMatch(Signature[] signatures,
109 List<HashSet<Signature>> sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700110 if (signatures == null) return false;
111
112 // build hashset of input to test against
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700113 HashSet<Signature> inputSet = new HashSet<>();
114 Collections.addAll(inputSet, signatures);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700115
116 // test input against each of the signature sets
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500117 for (HashSet<Signature> referenceSet : sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700118 if (referenceSet.equals(inputSet)) {
119 return true;
120 }
121 }
122 return false;
123 }
124
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700125 private final Context mContext;
126 private final String mTag;
127 private final String mAction;
128 private final String mServicePackageName;
129 private final List<HashSet<Signature>> mSignatureSets;
130
131 private final Handler mHandler;
132
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700133 // read/write from handler thread
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800134 private IBinder mBestService;
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700135 private int mCurrentUserId;
136
137 // read from any thread, write from handler thread
138 private volatile ComponentName mBestComponent;
139 private volatile int mBestVersion;
140 private volatile int mBestUserId;
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700141
142 public ServiceWatcher(Context context, String logTag, String action,
143 int overlaySwitchResId, int defaultServicePackageNameResId,
144 int initialPackageNamesResId, Handler handler) {
145 Resources resources = context.getResources();
146
147 mContext = context;
148 mTag = logTag;
149 mAction = action;
150
151 boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
152 if (enableOverlay) {
153 String[] pkgs = resources.getStringArray(initialPackageNamesResId);
154 mServicePackageName = null;
155 mSignatureSets = getSignatureSets(context, pkgs);
156 if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
157 } else {
158 mServicePackageName = resources.getString(defaultServicePackageNameResId);
159 mSignatureSets = getSignatureSets(context, mServicePackageName);
160 if (D) Log.d(mTag, "Overlay disabled, default package=" + mServicePackageName);
161 }
162
163 mHandler = handler;
164
165 mBestComponent = null;
166 mBestVersion = Integer.MIN_VALUE;
167 mBestUserId = UserHandle.USER_NULL;
168
169 mBestService = null;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500170 }
171
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800172 protected void onBind() {}
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700173
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800174 protected void onUnbind() {}
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700175
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700176 /**
177 * Start this watcher, including binding to the current best match and
178 * re-binding to any better matches down the road.
179 * <p>
180 * Note that if there are no matching encryption-aware services, we may not
181 * bind to a real service until after the current user is unlocked.
182 *
183 * @return {@code true} if a potential service implementation was found.
184 */
185 public final boolean start() {
186 // if we have to return false, do it before registering anything
187 if (isServiceMissing()) return false;
188
189 // listen for relevant package changes if service overlay is enabled on handler
190 if (mServicePackageName == null) {
191 new PackageMonitor() {
192 @Override
193 public void onPackageUpdateFinished(String packageName, int uid) {
194 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700195 }
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700196
197 @Override
198 public void onPackageAdded(String packageName, int uid) {
199 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
200 }
201
202 @Override
203 public void onPackageRemoved(String packageName, int uid) {
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700204 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700205 }
206
207 @Override
208 public boolean onPackageChanged(String packageName, int uid, String[] components) {
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700209 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700210 return super.onPackageChanged(packageName, uid, components);
211 }
212 }.register(mContext, UserHandle.ALL, true, mHandler);
213 }
214
215 // listen for user change on handler
216 IntentFilter intentFilter = new IntentFilter();
217 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
218 intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
219 mContext.registerReceiverAsUser(new BroadcastReceiver() {
220 @Override
221 public void onReceive(Context context, Intent intent) {
222 final String action = intent.getAction();
223 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
224 UserHandle.USER_NULL);
225 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
226 mCurrentUserId = userId;
227 bindBestPackage(false);
228 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
229 if (userId == mCurrentUserId) {
230 bindBestPackage(false);
231 }
232 }
233 }
234 }, UserHandle.ALL, intentFilter, null, mHandler);
235
236 mCurrentUserId = ActivityManager.getCurrentUser();
237
238 mHandler.post(() -> bindBestPackage(false));
239 return true;
240 }
241
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700242 /** Returns the name of the currently connected package or null. */
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700243 @Nullable
244 public String getCurrentPackageName() {
245 ComponentName bestComponent = mBestComponent;
246 return bestComponent == null ? null : bestComponent.getPackageName();
247 }
248
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700249 private boolean isServiceMissing() {
250 return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction),
251 PackageManager.MATCH_DIRECT_BOOT_AWARE
252 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
253 UserHandle.USER_SYSTEM).isEmpty();
254 }
255
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700256 private void bindBestPackage(boolean forceRebind) {
257 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
258
259 Intent intent = new Intent(mAction);
260 if (mServicePackageName != null) {
261 intent.setPackage(mServicePackageName);
262 }
263
264 List<ResolveInfo> rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent,
265 PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO,
266 mCurrentUserId);
267 if (rInfos == null) {
268 rInfos = Collections.emptyList();
269 }
270
271 ComponentName bestComponent = null;
272 int bestVersion = Integer.MIN_VALUE;
273 boolean bestIsMultiuser = false;
274
275 for (ResolveInfo rInfo : rInfos) {
276 ComponentName component = rInfo.serviceInfo.getComponentName();
277 String packageName = component.getPackageName();
278
279 // check signature
280 try {
281 PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(packageName,
282 PackageManager.GET_SIGNATURES
283 | PackageManager.MATCH_DIRECT_BOOT_AUTO);
284 if (!isSignatureMatch(pInfo.signatures, mSignatureSets)) {
285 Log.w(mTag, packageName + " resolves service " + mAction
286 + ", but has wrong signature, ignoring");
287 continue;
288 }
289 } catch (NameNotFoundException e) {
290 Log.wtf(mTag, e);
291 continue;
292 }
293
294 // check metadata
295 Bundle metadata = rInfo.serviceInfo.metaData;
296 int version = Integer.MIN_VALUE;
297 boolean isMultiuser = false;
298 if (metadata != null) {
299 version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
300 isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
301 }
302
303 if (version > bestVersion) {
304 bestComponent = component;
305 bestVersion = version;
306 bestIsMultiuser = isMultiuser;
307 }
308 }
309
310 if (D) {
311 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
312 (mServicePackageName == null ? ""
313 : "(" + mServicePackageName + ") "), rInfos.size(),
314 (bestComponent == null ? "no new best component"
315 : "new best component: " + bestComponent)));
316 }
317
318 if (bestComponent == null) {
319 Slog.w(mTag, "Odd, no component found for service " + mAction);
320 unbind();
321 return;
322 }
323
324 int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
325 boolean alreadyBound = Objects.equals(bestComponent, mBestComponent)
326 && bestVersion == mBestVersion && userId == mBestUserId;
327 if (forceRebind || !alreadyBound) {
328 unbind();
329 bind(bestComponent, bestVersion, userId);
330 }
331 }
332
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700333 private void bind(ComponentName component, int version, int userId) {
334 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
335
336 Intent intent = new Intent(mAction);
337 intent.setComponent(component);
338
339 mBestComponent = component;
340 mBestVersion = version;
341 mBestUserId = userId;
342
343 if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
344 mContext.bindServiceAsUser(intent, this,
345 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
346 UserHandle.of(userId));
347 }
348
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700349 private void unbind() {
350 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
351
352 if (mBestComponent != null) {
353 if (D) Log.d(mTag, "unbinding " + mBestComponent);
354 mContext.unbindService(this);
355 }
356
357 mBestComponent = null;
358 mBestVersion = Integer.MIN_VALUE;
359 mBestUserId = UserHandle.USER_NULL;
360 }
361
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800362 /**
363 * Runs the given function asynchronously if currently connected. Suppresses any RemoteException
364 * thrown during execution.
365 */
366 public final void runOnBinder(BinderRunner runner) {
367 runOnHandler(() -> {
368 if (mBestService == null) {
369 return;
370 }
371
372 try {
373 runner.run(mBestService);
374 } catch (RuntimeException e) {
375 // the code being run is privileged, but may be outside the system server, and thus
376 // we cannot allow runtime exceptions to crash the system server
377 Log.e(TAG, "exception while while running " + runner + " on " + mBestService
378 + " from " + this, e);
379 } catch (RemoteException e) {
380 // do nothing
381 }
382 });
383 }
384
385 /**
386 * Runs the given function synchronously if currently connected, and returns the default value
387 * if not currently connected or if any exception is thrown.
Soonil Nagarkarb4707182019-04-02 20:50:02 -0700388 *
389 * @deprecated Using this function is an indication that your AIDL API is broken. Calls from
390 * system server to outside MUST be one-way, and so cannot return any result, and this
391 * method should not be needed or used. Use a separate callback interface to allow outside
392 * components to return results back to the system server.
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800393 */
Soonil Nagarkarb4707182019-04-02 20:50:02 -0700394 @Deprecated
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800395 public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) {
396 try {
397 return runOnHandlerBlocking(() -> {
398 if (mBestService == null) {
399 return defaultValue;
400 }
401
402 try {
403 return runner.run(mBestService);
404 } catch (RemoteException e) {
405 return defaultValue;
406 }
407 });
Soonil Nagarkar93ff5b82019-10-29 13:34:26 -0700408 } catch (InterruptedException | TimeoutException e) {
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800409 return defaultValue;
410 }
411 }
412
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700413 @Override
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700414 public final void onServiceConnected(ComponentName component, IBinder binder) {
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800415 runOnHandler(() -> {
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700416 if (D) Log.d(mTag, component + " connected");
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800417 mBestService = binder;
418 onBind();
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700419 });
420 }
421
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700422 @Override
423 public final void onServiceDisconnected(ComponentName component) {
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800424 runOnHandler(() -> {
Jeff Sharkey58482c552016-02-08 17:49:17 -0700425 if (D) Log.d(mTag, component + " disconnected");
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700426 mBestService = null;
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800427 onUnbind();
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700428 });
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700429 }
430
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700431 @Override
432 public String toString() {
433 ComponentName bestComponent = mBestComponent;
434 return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
Victoria Leaseb711d572012-10-02 13:14:11 -0700435 }
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800436
437 private void runOnHandler(Runnable r) {
438 if (Looper.myLooper() == mHandler.getLooper()) {
439 r.run();
440 } else {
441 mHandler.post(r);
442 }
443 }
444
Soonil Nagarkar93ff5b82019-10-29 13:34:26 -0700445 private <T> T runOnHandlerBlocking(Callable<T> c)
446 throws InterruptedException, TimeoutException {
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800447 if (Looper.myLooper() == mHandler.getLooper()) {
448 try {
449 return c.call();
450 } catch (Exception e) {
451 // Function cannot throw exception, this should never happen
452 throw new IllegalStateException(e);
453 }
454 } else {
455 FutureTask<T> task = new FutureTask<>(c);
456 mHandler.post(task);
457 try {
Soonil Nagarkar93ff5b82019-10-29 13:34:26 -0700458 // timeout will unblock callers, in particular if the caller is a binder thread to
459 // help reduce binder contention. this will still result in blocking the handler
460 // thread which may result in ANRs, but should make problems slightly more rare.
461 // the underlying solution is simply not to use this API at all, but that would
462 // require large refactors to very legacy code.
463 return task.get(BLOCKING_BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800464 } catch (ExecutionException e) {
465 // Function cannot throw exception, this should never happen
466 throw new IllegalStateException(e);
467 }
468 }
469 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700470}