blob: d09823efb6fabe314598fa7223b9d4c68590ed7d [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;
Victoria Leaseb711d572012-10-02 13:14:11 -070037import android.os.UserHandle;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070038import android.util.Log;
Jeff Sharkey58482c552016-02-08 17:49:17 -070039import android.util.Slog;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070040
Jeff Sharkey58482c552016-02-08 17:49:17 -070041import com.android.internal.annotations.GuardedBy;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070042import 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;
Nick Pelly6fa9ad42012-07-16 12:18:23 -070051
52/**
53 * Find the best Service, and bind to it.
54 * Handles run-time package changes.
55 */
56public class ServiceWatcher implements ServiceConnection {
Soonil Nagarkare731ca82018-11-02 13:55:51 -070057
58 private static final String TAG = "ServiceWatcher";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070059 private static final boolean D = false;
Soonil Nagarkare731ca82018-11-02 13:55:51 -070060
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050061 public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
Victoria Lease03cdd3d2013-02-01 15:15:54 -080062 public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
Nick Pelly6fa9ad42012-07-16 12:18:23 -070063
Zhentao Sunc5fc9982013-04-17 17:47:53 -070064 /**
Soonil Nagarkare731ca82018-11-02 13:55:51 -070065 * The runner that runs on the binder retrieved from {@link ServiceWatcher}.
Zhentao Sunc5fc9982013-04-17 17:47:53 -070066 */
Soonil Nagarkare731ca82018-11-02 13:55:51 -070067 public interface BinderRunner {
68 /**
69 * Runs on the retrieved binder.
70 *
71 * @param binder the binder retrieved from the {@link ServiceWatcher}.
72 */
73 void run(IBinder binder);
74 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -070075
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050076 public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
Soonil Nagarkare731ca82018-11-02 13:55:51 -070077 String... packageNames) {
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050078 PackageManager pm = context.getPackageManager();
Soonil Nagarkare731ca82018-11-02 13:55:51 -070079
80 ArrayList<HashSet<Signature>> signatureSets = new ArrayList<>(packageNames.length);
81 for (String packageName : packageNames) {
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050082 try {
Soonil Nagarkare731ca82018-11-02 13:55:51 -070083 Signature[] signatures = pm.getPackageInfo(packageName,
84 PackageManager.MATCH_SYSTEM_ONLY
85 | PackageManager.GET_SIGNATURES).signatures;
86
87 HashSet<Signature> set = new HashSet<>();
88 Collections.addAll(set, signatures);
89 signatureSets.add(set);
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050090 } catch (NameNotFoundException e) {
Soonil Nagarkare731ca82018-11-02 13:55:51 -070091 Log.w(TAG, packageName + " not found");
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050092 }
93 }
Soonil Nagarkare731ca82018-11-02 13:55:51 -070094 return signatureSets;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050095 }
96
Soonil Nagarkare731ca82018-11-02 13:55:51 -070097 /** Checks if signatures match. */
Jeff Hamiltonfbadb692012-10-05 14:21:58 -050098 public static boolean isSignatureMatch(Signature[] signatures,
99 List<HashSet<Signature>> sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700100 if (signatures == null) return false;
101
102 // build hashset of input to test against
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700103 HashSet<Signature> inputSet = new HashSet<>();
104 Collections.addAll(inputSet, signatures);
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700105
106 // test input against each of the signature sets
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500107 for (HashSet<Signature> referenceSet : sigSets) {
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700108 if (referenceSet.equals(inputSet)) {
109 return true;
110 }
111 }
112 return false;
113 }
114
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700115 private final Context mContext;
116 private final String mTag;
117 private final String mAction;
118 private final String mServicePackageName;
119 private final List<HashSet<Signature>> mSignatureSets;
120
121 private final Handler mHandler;
122
123 // this lock is held to ensure the service binder is not exposed (via runOnBinder) until after
124 // the new service initialization work has completed
125 private final Object mBindLock = new Object();
126
127 // read/write from handler thread
128 private int mCurrentUserId;
129
130 // read from any thread, write from handler thread
131 private volatile ComponentName mBestComponent;
132 private volatile int mBestVersion;
133 private volatile int mBestUserId;
134 private volatile IBinder mBestService;
135
136 public ServiceWatcher(Context context, String logTag, String action,
137 int overlaySwitchResId, int defaultServicePackageNameResId,
138 int initialPackageNamesResId, Handler handler) {
139 Resources resources = context.getResources();
140
141 mContext = context;
142 mTag = logTag;
143 mAction = action;
144
145 boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
146 if (enableOverlay) {
147 String[] pkgs = resources.getStringArray(initialPackageNamesResId);
148 mServicePackageName = null;
149 mSignatureSets = getSignatureSets(context, pkgs);
150 if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
151 } else {
152 mServicePackageName = resources.getString(defaultServicePackageNameResId);
153 mSignatureSets = getSignatureSets(context, mServicePackageName);
154 if (D) Log.d(mTag, "Overlay disabled, default package=" + mServicePackageName);
155 }
156
157 mHandler = handler;
158
159 mBestComponent = null;
160 mBestVersion = Integer.MIN_VALUE;
161 mBestUserId = UserHandle.USER_NULL;
162
163 mBestService = null;
Jeff Hamiltonfbadb692012-10-05 14:21:58 -0500164 }
165
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700166 // called on handler thread
167 @GuardedBy("mBindLock")
168 protected void onBind() {
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700169 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700170 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700171
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700172 // called on handler thread
173 @GuardedBy("mBindLock")
174 protected void onUnbind() {
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700175 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700176 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700177
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700178 /**
179 * Start this watcher, including binding to the current best match and
180 * re-binding to any better matches down the road.
181 * <p>
182 * Note that if there are no matching encryption-aware services, we may not
183 * bind to a real service until after the current user is unlocked.
184 *
185 * @return {@code true} if a potential service implementation was found.
186 */
187 public final boolean start() {
188 // if we have to return false, do it before registering anything
189 if (isServiceMissing()) return false;
190
191 // listen for relevant package changes if service overlay is enabled on handler
192 if (mServicePackageName == null) {
193 new PackageMonitor() {
194 @Override
195 public void onPackageUpdateFinished(String packageName, int uid) {
196 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700197 }
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700198
199 @Override
200 public void onPackageAdded(String packageName, int uid) {
201 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
202 }
203
204 @Override
205 public void onPackageRemoved(String packageName, int uid) {
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700206 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700207 }
208
209 @Override
210 public boolean onPackageChanged(String packageName, int uid, String[] components) {
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700211 bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700212 return super.onPackageChanged(packageName, uid, components);
213 }
214 }.register(mContext, UserHandle.ALL, true, mHandler);
215 }
216
217 // listen for user change on handler
218 IntentFilter intentFilter = new IntentFilter();
219 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
220 intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
221 mContext.registerReceiverAsUser(new BroadcastReceiver() {
222 @Override
223 public void onReceive(Context context, Intent intent) {
224 final String action = intent.getAction();
225 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
226 UserHandle.USER_NULL);
227 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
228 mCurrentUserId = userId;
229 bindBestPackage(false);
230 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
231 if (userId == mCurrentUserId) {
232 bindBestPackage(false);
233 }
234 }
235 }
236 }, UserHandle.ALL, intentFilter, null, mHandler);
237
238 mCurrentUserId = ActivityManager.getCurrentUser();
239
240 mHandler.post(() -> bindBestPackage(false));
241 return true;
242 }
243
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700244 /** Returns the name of the currently connected package or null. */
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700245 @Nullable
246 public String getCurrentPackageName() {
247 ComponentName bestComponent = mBestComponent;
248 return bestComponent == null ? null : bestComponent.getPackageName();
249 }
250
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700251 /**
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700252 * Runs the given BinderRunner if currently connected. All invocations to runOnBinder are run
253 * serially.
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700254 */
255 public final void runOnBinder(BinderRunner runner) {
256 synchronized (mBindLock) {
257 IBinder service = mBestService;
258 if (service != null) {
259 try {
260 runner.run(service);
261 } catch (Exception e) {
262 // remote exceptions cannot be allowed to crash system server
263 Log.e(TAG, "exception while while running " + runner + " on " + service
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700264 + " from " + this, e);
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700265 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700266 }
267 }
268 }
269
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700270 private boolean isServiceMissing() {
271 return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction),
272 PackageManager.MATCH_DIRECT_BOOT_AWARE
273 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
274 UserHandle.USER_SYSTEM).isEmpty();
275 }
276
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700277 private void bindBestPackage(boolean forceRebind) {
278 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
279
280 Intent intent = new Intent(mAction);
281 if (mServicePackageName != null) {
282 intent.setPackage(mServicePackageName);
283 }
284
285 List<ResolveInfo> rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent,
286 PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO,
287 mCurrentUserId);
288 if (rInfos == null) {
289 rInfos = Collections.emptyList();
290 }
291
292 ComponentName bestComponent = null;
293 int bestVersion = Integer.MIN_VALUE;
294 boolean bestIsMultiuser = false;
295
296 for (ResolveInfo rInfo : rInfos) {
297 ComponentName component = rInfo.serviceInfo.getComponentName();
298 String packageName = component.getPackageName();
299
300 // check signature
301 try {
302 PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(packageName,
303 PackageManager.GET_SIGNATURES
304 | PackageManager.MATCH_DIRECT_BOOT_AUTO);
305 if (!isSignatureMatch(pInfo.signatures, mSignatureSets)) {
306 Log.w(mTag, packageName + " resolves service " + mAction
307 + ", but has wrong signature, ignoring");
308 continue;
309 }
310 } catch (NameNotFoundException e) {
311 Log.wtf(mTag, e);
312 continue;
313 }
314
315 // check metadata
316 Bundle metadata = rInfo.serviceInfo.metaData;
317 int version = Integer.MIN_VALUE;
318 boolean isMultiuser = false;
319 if (metadata != null) {
320 version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
321 isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
322 }
323
324 if (version > bestVersion) {
325 bestComponent = component;
326 bestVersion = version;
327 bestIsMultiuser = isMultiuser;
328 }
329 }
330
331 if (D) {
332 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
333 (mServicePackageName == null ? ""
334 : "(" + mServicePackageName + ") "), rInfos.size(),
335 (bestComponent == null ? "no new best component"
336 : "new best component: " + bestComponent)));
337 }
338
339 if (bestComponent == null) {
340 Slog.w(mTag, "Odd, no component found for service " + mAction);
341 unbind();
342 return;
343 }
344
345 int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
346 boolean alreadyBound = Objects.equals(bestComponent, mBestComponent)
347 && bestVersion == mBestVersion && userId == mBestUserId;
348 if (forceRebind || !alreadyBound) {
349 unbind();
350 bind(bestComponent, bestVersion, userId);
351 }
352 }
353
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700354 private void bind(ComponentName component, int version, int userId) {
355 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
356
357 Intent intent = new Intent(mAction);
358 intent.setComponent(component);
359
360 mBestComponent = component;
361 mBestVersion = version;
362 mBestUserId = userId;
363
364 if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
365 mContext.bindServiceAsUser(intent, this,
366 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
367 UserHandle.of(userId));
368 }
369
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700370 private void unbind() {
371 Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
372
373 if (mBestComponent != null) {
374 if (D) Log.d(mTag, "unbinding " + mBestComponent);
375 mContext.unbindService(this);
376 }
377
378 mBestComponent = null;
379 mBestVersion = Integer.MIN_VALUE;
380 mBestUserId = UserHandle.USER_NULL;
381 }
382
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700383 @Override
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700384 public final void onServiceConnected(ComponentName component, IBinder binder) {
385 mHandler.post(() -> {
386 if (D) Log.d(mTag, component + " connected");
387
388 // hold the lock so that mBestService cannot be used by runOnBinder until complete
389 synchronized (mBindLock) {
390 mBestService = binder;
391 onBind();
392 }
393 });
394 }
395
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700396 @Override
397 public final void onServiceDisconnected(ComponentName component) {
398 mHandler.post(() -> {
Jeff Sharkey58482c552016-02-08 17:49:17 -0700399 if (D) Log.d(mTag, component + " disconnected");
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700400
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700401 mBestService = null;
402 synchronized (mBindLock) {
403 onUnbind();
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700404 }
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700405 });
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700406 }
407
Soonil Nagarkare731ca82018-11-02 13:55:51 -0700408 @Override
409 public String toString() {
410 ComponentName bestComponent = mBestComponent;
411 return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
Victoria Leaseb711d572012-10-02 13:14:11 -0700412 }
Nick Pelly6fa9ad42012-07-16 12:18:23 -0700413}