1 /*
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 
17 package com.android.server;
18 
19 import android.annotation.Nullable;
20 import android.app.ActivityManager;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.ServiceConnection;
27 import android.content.pm.PackageInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.PackageManager.NameNotFoundException;
30 import android.content.pm.ResolveInfo;
31 import android.content.pm.Signature;
32 import android.content.res.Resources;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.os.IBinder;
36 import android.os.Looper;
37 import android.os.RemoteException;
38 import android.os.UserHandle;
39 import android.util.Log;
40 import android.util.Slog;
41 
42 import com.android.internal.content.PackageMonitor;
43 import com.android.internal.util.Preconditions;
44 
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Objects;
51 import java.util.concurrent.Callable;
52 import java.util.concurrent.ExecutionException;
53 import java.util.concurrent.FutureTask;
54 
55 /**
56  * Find the best Service, and bind to it.
57  * Handles run-time package changes.
58  */
59 public class ServiceWatcher implements ServiceConnection {
60 
61     private static final String TAG = "ServiceWatcher";
62     private static final boolean D = false;
63 
64     public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
65     public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
66 
67 
68     /** Function to run on binder interface. */
69     public interface BinderRunner {
70         /** Called to run client code with the binder. */
run(IBinder 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. */
run(IBinder binder)80         T run(IBinder binder) throws RemoteException;
81     }
82 
getSignatureSets(Context context, String... packageNames)83     public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
84             String... packageNames) {
85         PackageManager pm = context.getPackageManager();
86 
87         ArrayList<HashSet<Signature>> signatureSets = new ArrayList<>(packageNames.length);
88         for (String packageName : packageNames) {
89             try {
90                 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);
97             } catch (NameNotFoundException e) {
98                 Log.w(TAG, packageName + " not found");
99             }
100         }
101         return signatureSets;
102     }
103 
104     /** Checks if signatures match. */
isSignatureMatch(Signature[] signatures, List<HashSet<Signature>> sigSets)105     public static boolean isSignatureMatch(Signature[] signatures,
106             List<HashSet<Signature>> sigSets) {
107         if (signatures == null) return false;
108 
109         // build hashset of input to test against
110         HashSet<Signature> inputSet = new HashSet<>();
111         Collections.addAll(inputSet, signatures);
112 
113         // test input against each of the signature sets
114         for (HashSet<Signature> referenceSet : sigSets) {
115             if (referenceSet.equals(inputSet)) {
116                 return true;
117             }
118         }
119         return false;
120     }
121 
122     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 
130     // read/write from handler thread
131     private IBinder mBestService;
132     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;
138 
ServiceWatcher(Context context, String logTag, String action, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId, Handler handler)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;
167     }
168 
onBind()169     protected void onBind() {}
170 
onUnbind()171     protected void onUnbind() {}
172 
173     /**
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      */
start()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()));
192                 }
193 
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) {
201                     bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
202                 }
203 
204                 @Override
205                 public boolean onPackageChanged(String packageName, int uid, String[] components) {
206                     bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
207                     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 
239     /** Returns the name of the currently connected package or null. */
240     @Nullable
getCurrentPackageName()241     public String getCurrentPackageName() {
242         ComponentName bestComponent = mBestComponent;
243         return bestComponent == null ? null : bestComponent.getPackageName();
244     }
245 
isServiceMissing()246     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 
bindBestPackage(boolean forceRebind)253     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 
bind(ComponentName component, int version, int userId)330     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 
unbind()346     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 
359     /**
360      * Runs the given function asynchronously if currently connected. Suppresses any RemoteException
361      * thrown during execution.
362      */
runOnBinder(BinderRunner runner)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      * @deprecated Using this function is an indication that your AIDL API is broken. Calls from
387      * system server to outside MUST be one-way, and so cannot return any result, and this
388      * method should not be needed or used. Use a separate callback interface to allow outside
389      * components to return results back to the system server.
390      */
391     @Deprecated
runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue)392     public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) {
393         try {
394             return runOnHandlerBlocking(() -> {
395                 if (mBestService == null) {
396                     return defaultValue;
397                 }
398 
399                 try {
400                     return runner.run(mBestService);
401                 } catch (RemoteException e) {
402                     return defaultValue;
403                 }
404             });
405         } catch (InterruptedException e) {
406             return defaultValue;
407         }
408     }
409 
410     @Override
411     public final void onServiceConnected(ComponentName component, IBinder binder) {
412         runOnHandler(() -> {
413             if (D) Log.d(mTag, component + " connected");
414             mBestService = binder;
415             onBind();
416         });
417     }
418 
419     @Override
420     public final void onServiceDisconnected(ComponentName component) {
421         runOnHandler(() -> {
422             if (D) Log.d(mTag, component + " disconnected");
423             mBestService = null;
424             onUnbind();
425         });
426     }
427 
428     @Override
429     public String toString() {
430         ComponentName bestComponent = mBestComponent;
431         return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
432     }
433 
434     private void runOnHandler(Runnable r) {
435         if (Looper.myLooper() == mHandler.getLooper()) {
436             r.run();
437         } else {
438             mHandler.post(r);
439         }
440     }
441 
442     private <T> T runOnHandlerBlocking(Callable<T> c) throws InterruptedException {
443         if (Looper.myLooper() == mHandler.getLooper()) {
444             try {
445                 return c.call();
446             } catch (Exception e) {
447                 // Function cannot throw exception, this should never happen
448                 throw new IllegalStateException(e);
449             }
450         } else {
451             FutureTask<T> task = new FutureTask<>(c);
452             mHandler.post(task);
453             try {
454                 return task.get();
455             } catch (ExecutionException e) {
456                 // Function cannot throw exception, this should never happen
457                 throw new IllegalStateException(e);
458             }
459         }
460     }
461 }
462