blob: a5a515f93e7576abf2fd807ee6b005346365e0b3 [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;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070054
55/**
56 * Find the best Service, and bind to it.
57 * Handles run-time package changes.
58 */
59public class ServiceWatcher implements ServiceConnection {
Soonil Nagarkare731ca82018-11-02 13:55:51 -070060
61 private static final String TAG = "ServiceWatcher";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070062 private static final boolean D = false;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070063
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050064 public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
Victoria Lease03cdd3d2013-02-01 15:15:54 -080065 public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070066
Soonil Nagarkar192710b2019-01-24 17:42:49 -080067
68 /** Function to run on binder interface. */
Soonil Nagarkare731ca82018-11-02 13:55:51 -070069 public interface BinderRunner {
Soonil Nagarkar192710b2019-01-24 17:42:49 -080070 /** Called to run client code with the binder. */
71 void run(IBinder binder) throws RemoteException;
72 }
73
74 /**
75 * Function to run on binder interface.
76 * @param <T> Type to return.
77 */
78 public interface BlockingBinderRunner<T> {
79 /** Called to run client code with the binder. */
80 T run(IBinder binder) throws RemoteException;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070081 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -070082
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050083 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
Soonil Nagarkare731ca82018-11-02 13:55:51 -070084 String... packageNames) {
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050085 PackageManager pm = context.getPackageManager();
Soonil Nagarkare731ca82018-11-02 13:55:51 -070086
87 ArrayList<HashSet<Signature>> signatureSets = new ArrayList<>(packageNames.length);
88 for (String packageName : packageNames) {
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050089 try {
Soonil Nagarkare731ca82018-11-02 13:55:51 -070090 Signature[] signatures = pm.getPackageInfo(packageName,
91 PackageManager.MATCH_SYSTEM_ONLY
92 | PackageManager.GET_SIGNATURES).signatures;
93
94 HashSet<Signature> set = new HashSet<>();
95 Collections.addAll(set, signatures);
96 signatureSets.add(set);
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050097 } catch (NameNotFoundException e) {
Soonil Nagarkare731ca82018-11-02 13:55:51 -070098 Log.w(TAG, packageName + " not found");
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050099 }
100 }
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700101 return signatureSets;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500102 }
103
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700104 /** Checks if signatures match. */
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500105 public static boolean isSignatureMatch(Signature[] signatures,
106 List<HashSet<Signature>> sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700107 if (signatures == null) return false;
108
109 // build hashset of input to test against
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700110 HashSet<Signature> inputSet = new HashSet<>();
111 Collections.addAll(inputSet, signatures);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700112
113 // test input against each of the signature sets
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500114 for (HashSet<Signature> referenceSet : sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700115 if (referenceSet.equals(inputSet)) {
116 return true;
117 }
118 }
119 return false;
120 }
121
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700122 private final Context mContext;
123 private final String mTag;
124 private final String mAction;
125 private final String mServicePackageName;
126 private final List<HashSet<Signature>> mSignatureSets;
127
128 private final Handler mHandler;
129
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700130 // read/write from handler thread
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800131 private IBinder mBestService;
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700132 private int mCurrentUserId;
133
134 // read from any thread, write from handler thread
135 private volatile ComponentName mBestComponent;
136 private volatile int mBestVersion;
137 private volatile int mBestUserId;
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700138
139 public ServiceWatcher(Context context, String logTag, String action,
140 int overlaySwitchResId, int defaultServicePackageNameResId,
141 int initialPackageNamesResId, Handler handler) {
142 Resources resources = context.getResources();
143
144 mContext = context;
145 mTag = logTag;
146 mAction = action;
147
148 boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
149 if (enableOverlay) {
150 String[] pkgs = resources.getStringArray(initialPackageNamesResId);
151 mServicePackageName = null;
152 mSignatureSets = getSignatureSets(context, pkgs);
153 if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
154 } else {
155 mServicePackageName = resources.getString(defaultServicePackageNameResId);
156 mSignatureSets = getSignatureSets(context, mServicePackageName);
157 if (D) Log.d(mTag, "Overlay disabled, default package=" + mServicePackageName);
158 }
159
160 mHandler = handler;
161
162 mBestComponent = null;
163 mBestVersion = Integer.MIN_VALUE;
164 mBestUserId = UserHandle.USER_NULL;
165
166 mBestService = null;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500167 }
168
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800169 protected void onBind() {}
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700170
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800171 protected void onUnbind() {}
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700172
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700173 /**
174 * Start this watcher, including binding to the current best match and
175 * re-binding to any better matches down the road.
176 * <p>
177 * Note that if there are no matching encryption-aware services, we may not
178 * bind to a real service until after the current user is unlocked.
179 *
180 * @return {@code true} if a potential service implementation was found.
181 */
182 public final boolean start() {
183 // if we have to return false, do it before registering anything
184 if (isServiceMissing()) return false;
185
186 // listen for relevant package changes if service overlay is enabled on handler
187 if (mServicePackageName == null) {
188 new PackageMonitor() {
189 @Override
190 public void onPackageUpdateFinished(String packageName, int uid) {
191 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700192 }
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700193
194 @Override
195 public void onPackageAdded(String packageName, int uid) {
196 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
197 }
198
199 @Override
200 public void onPackageRemoved(String packageName, int uid) {
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700201 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700202 }
203
204 @Override
205 public boolean onPackageChanged(String packageName, int uid, String[] components) {
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700206 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700207 return super.onPackageChanged(packageName, uid, components);
208 }
209 }.register(mContext, UserHandle.ALL, true, mHandler);
210 }
211
212 // listen for user change on handler
213 IntentFilter intentFilter = new IntentFilter();
214 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
215 intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
216 mContext.registerReceiverAsUser(new BroadcastReceiver() {
217 @Override
218 public void onReceive(Context context, Intent intent) {
219 final String action = intent.getAction();
220 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
221 UserHandle.USER_NULL);
222 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
223 mCurrentUserId = userId;
224 bindBestPackage(false);
225 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
226 if (userId == mCurrentUserId) {
227 bindBestPackage(false);
228 }
229 }
230 }
231 }, UserHandle.ALL, intentFilter, null, mHandler);
232
233 mCurrentUserId = ActivityManager.getCurrentUser();
234
235 mHandler.post(() -> bindBestPackage(false));
236 return true;
237 }
238
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700239 /** Returns the name of the currently connected package or null. */
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700240 @Nullable
241 public String getCurrentPackageName() {
242 ComponentName bestComponent = mBestComponent;
243 return bestComponent == null ? null : bestComponent.getPackageName();
244 }
245
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700246 private boolean isServiceMissing() {
247 return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction),
248 PackageManager.MATCH_DIRECT_BOOT_AWARE
249 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
250 UserHandle.USER_SYSTEM).isEmpty();
251 }
252
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700253 private void bindBestPackage(boolean forceRebind) {
254 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
255
256 Intent intent = new Intent(mAction);
257 if (mServicePackageName != null) {
258 intent.setPackage(mServicePackageName);
259 }
260
261 List<ResolveInfo> rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent,
262 PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO,
263 mCurrentUserId);
264 if (rInfos == null) {
265 rInfos = Collections.emptyList();
266 }
267
268 ComponentName bestComponent = null;
269 int bestVersion = Integer.MIN_VALUE;
270 boolean bestIsMultiuser = false;
271
272 for (ResolveInfo rInfo : rInfos) {
273 ComponentName component = rInfo.serviceInfo.getComponentName();
274 String packageName = component.getPackageName();
275
276 // check signature
277 try {
278 PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(packageName,
279 PackageManager.GET_SIGNATURES
280 | PackageManager.MATCH_DIRECT_BOOT_AUTO);
281 if (!isSignatureMatch(pInfo.signatures, mSignatureSets)) {
282 Log.w(mTag, packageName + " resolves service " + mAction
283 + ", but has wrong signature, ignoring");
284 continue;
285 }
286 } catch (NameNotFoundException e) {
287 Log.wtf(mTag, e);
288 continue;
289 }
290
291 // check metadata
292 Bundle metadata = rInfo.serviceInfo.metaData;
293 int version = Integer.MIN_VALUE;
294 boolean isMultiuser = false;
295 if (metadata != null) {
296 version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
297 isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
298 }
299
300 if (version > bestVersion) {
301 bestComponent = component;
302 bestVersion = version;
303 bestIsMultiuser = isMultiuser;
304 }
305 }
306
307 if (D) {
308 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
309 (mServicePackageName == null ? ""
310 : "(" + mServicePackageName + ") "), rInfos.size(),
311 (bestComponent == null ? "no new best component"
312 : "new best component: " + bestComponent)));
313 }
314
315 if (bestComponent == null) {
316 Slog.w(mTag, "Odd, no component found for service " + mAction);
317 unbind();
318 return;
319 }
320
321 int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
322 boolean alreadyBound = Objects.equals(bestComponent, mBestComponent)
323 && bestVersion == mBestVersion && userId == mBestUserId;
324 if (forceRebind || !alreadyBound) {
325 unbind();
326 bind(bestComponent, bestVersion, userId);
327 }
328 }
329
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700330 private void bind(ComponentName component, int version, int userId) {
331 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
332
333 Intent intent = new Intent(mAction);
334 intent.setComponent(component);
335
336 mBestComponent = component;
337 mBestVersion = version;
338 mBestUserId = userId;
339
340 if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
341 mContext.bindServiceAsUser(intent, this,
342 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
343 UserHandle.of(userId));
344 }
345
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700346 private void unbind() {
347 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
348
349 if (mBestComponent != null) {
350 if (D) Log.d(mTag, "unbinding " + mBestComponent);
351 mContext.unbindService(this);
352 }
353
354 mBestComponent = null;
355 mBestVersion = Integer.MIN_VALUE;
356 mBestUserId = UserHandle.USER_NULL;
357 }
358
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800359 /**
360 * Runs the given function asynchronously if currently connected. Suppresses any RemoteException
361 * thrown during execution.
362 */
363 public final void runOnBinder(BinderRunner runner) {
364 runOnHandler(() -> {
365 if (mBestService == null) {
366 return;
367 }
368
369 try {
370 runner.run(mBestService);
371 } catch (RuntimeException e) {
372 // the code being run is privileged, but may be outside the system server, and thus
373 // we cannot allow runtime exceptions to crash the system server
374 Log.e(TAG, "exception while while running " + runner + " on " + mBestService
375 + " from " + this, e);
376 } catch (RemoteException e) {
377 // do nothing
378 }
379 });
380 }
381
382 /**
383 * Runs the given function synchronously if currently connected, and returns the default value
384 * if not currently connected or if any exception is thrown.
385 */
386 public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) {
387 try {
388 return runOnHandlerBlocking(() -> {
389 if (mBestService == null) {
390 return defaultValue;
391 }
392
393 try {
394 return runner.run(mBestService);
395 } catch (RemoteException e) {
396 return defaultValue;
397 }
398 });
399 } catch (InterruptedException e) {
400 return defaultValue;
401 }
402 }
403
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700404 @Override
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700405 public final void onServiceConnected(ComponentName component, IBinder binder) {
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800406 runOnHandler(() -> {
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700407 if (D) Log.d(mTag, component + " connected");
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800408 mBestService = binder;
409 onBind();
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700410 });
411 }
412
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700413 @Override
414 public final void onServiceDisconnected(ComponentName component) {
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800415 runOnHandler(() -> {
Jeff Sharkey58482c552016-02-08 17:49:17 -0700416 if (D) Log.d(mTag, component + " disconnected");
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700417 mBestService = null;
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800418 onUnbind();
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700419 });
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700420 }
421
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700422 @Override
423 public String toString() {
424 ComponentName bestComponent = mBestComponent;
425 return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
Victoria Leaseb711d572012-10-02 13:14:11 -0700426 }
Soonil Nagarkar192710b2019-01-24 17:42:49 -0800427
428 private void runOnHandler(Runnable r) {
429 if (Looper.myLooper() == mHandler.getLooper()) {
430 r.run();
431 } else {
432 mHandler.post(r);
433 }
434 }
435
436 private <T> T runOnHandlerBlocking(Callable<T> c) throws InterruptedException {
437 if (Looper.myLooper() == mHandler.getLooper()) {
438 try {
439 return c.call();
440 } catch (Exception e) {
441 // Function cannot throw exception, this should never happen
442 throw new IllegalStateException(e);
443 }
444 } else {
445 FutureTask<T> task = new FutureTask<>(c);
446 mHandler.post(task);
447 try {
448 return task.get();
449 } catch (ExecutionException e) {
450 // Function cannot throw exception, this should never happen
451 throw new IllegalStateException(e);
452 }
453 }
454 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700455}