• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.ServiceConnection;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.pm.ResolveInfo;
29 import android.content.pm.Signature;
30 import android.content.res.Resources;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.UserHandle;
34 import android.util.Log;
35 
36 import com.android.internal.content.PackageMonitor;
37 
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.HashSet;
42 import java.util.List;
43 
44 /**
45  * Find the best Service, and bind to it.
46  * Handles run-time package changes.
47  */
48 public class ServiceWatcher implements ServiceConnection {
49     private static final boolean D = false;
50     public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
51     public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
52 
53     private final String mTag;
54     private final Context mContext;
55     private final PackageManager mPm;
56     private final List<HashSet<Signature>> mSignatureSets;
57     private final String mAction;
58 
59     /**
60      * If mServicePackageName is not null, only this package will be searched for the service that
61      * implements mAction. When null, all packages in the system that matches one of the signature
62      * in mSignatureSets are searched.
63      */
64     private final String mServicePackageName;
65     private final Runnable mNewServiceWork;
66     private final Handler mHandler;
67 
68     private Object mLock = new Object();
69 
70     // all fields below synchronized on mLock
71     private IBinder mBinder;   // connected service
72     private String mPackageName;  // current best package
73     private int mVersion = Integer.MIN_VALUE;  // current best version
74     /**
75      * Whether the currently-connected service is multiuser-aware. This can change at run-time
76      * when switching from one version of a service to another.
77      */
78     private boolean mIsMultiuser = false;
79 
getSignatureSets(Context context, List<String> initialPackageNames)80     public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
81             List<String> initialPackageNames) {
82         PackageManager pm = context.getPackageManager();
83         ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
84         for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
85             String pkg = initialPackageNames.get(i);
86             try {
87                 HashSet<Signature> set = new HashSet<Signature>();
88                 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
89                 set.addAll(Arrays.asList(sigs));
90                 sigSets.add(set);
91             } catch (NameNotFoundException e) {
92                 Log.w("ServiceWatcher", pkg + " not found");
93             }
94         }
95         return sigSets;
96     }
97 
ServiceWatcher(Context context, String logTag, String action, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId, Runnable newServiceWork, Handler handler)98     public ServiceWatcher(Context context, String logTag, String action,
99             int overlaySwitchResId, int defaultServicePackageNameResId,
100             int initialPackageNamesResId, Runnable newServiceWork,
101             Handler handler) {
102         mContext = context;
103         mTag = logTag;
104         mAction = action;
105         mPm = mContext.getPackageManager();
106         mNewServiceWork = newServiceWork;
107         mHandler = handler;
108         Resources resources = context.getResources();
109 
110         // Whether to enable service overlay.
111         boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
112         ArrayList<String>  initialPackageNames = new ArrayList<String>();
113         if (enableOverlay) {
114             // A list of package names used to create the signatures.
115             String[] pkgs = resources.getStringArray(initialPackageNamesResId);
116             if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
117             mServicePackageName = null;
118             if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
119         } else {
120             // The default package name that is searched for service implementation when overlay is
121             // disabled.
122             String servicePackageName = resources.getString(defaultServicePackageNameResId);
123             if (servicePackageName != null) initialPackageNames.add(servicePackageName);
124             mServicePackageName = servicePackageName;
125             if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
126         }
127         mSignatureSets = getSignatureSets(context, initialPackageNames);
128     }
129 
start()130     public boolean start() {
131         synchronized (mLock) {
132             if (!bindBestPackageLocked(mServicePackageName)) return false;
133         }
134 
135         // listen for user change
136         IntentFilter intentFilter = new IntentFilter();
137         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
138         mContext.registerReceiverAsUser(new BroadcastReceiver() {
139             @Override
140             public void onReceive(Context context, Intent intent) {
141                 String action = intent.getAction();
142                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
143                     switchUser();
144                 }
145             }
146         }, UserHandle.ALL, intentFilter, null, mHandler);
147 
148         // listen for relevant package changes if service overlay is enabled.
149         if (mServicePackageName == null) {
150             mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
151         }
152 
153         return true;
154     }
155 
156     /**
157      * Searches and binds to the best package, or do nothing
158      * if the best package is already bound.
159      * Only checks the named package, or checks all packages if it
160      * is null.
161      * Return true if a new package was found to bind to.
162      */
bindBestPackageLocked(String justCheckThisPackage)163     private boolean bindBestPackageLocked(String justCheckThisPackage) {
164         Intent intent = new Intent(mAction);
165         if (justCheckThisPackage != null) {
166             intent.setPackage(justCheckThisPackage);
167         }
168         List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
169                 PackageManager.GET_META_DATA, UserHandle.USER_OWNER);
170         int bestVersion = Integer.MIN_VALUE;
171         String bestPackage = null;
172         boolean bestIsMultiuser = false;
173         if (rInfos != null) {
174             for (ResolveInfo rInfo : rInfos) {
175                 String packageName = rInfo.serviceInfo.packageName;
176 
177                 // check signature
178                 try {
179                     PackageInfo pInfo;
180                     pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
181                     if (!isSignatureMatch(pInfo.signatures)) {
182                         Log.w(mTag, packageName + " resolves service " + mAction
183                                 + ", but has wrong signature, ignoring");
184                         continue;
185                     }
186                 } catch (NameNotFoundException e) {
187                     Log.wtf(mTag, e);
188                     continue;
189                 }
190 
191                 // check metadata
192                 int version = Integer.MIN_VALUE;
193                 boolean isMultiuser = false;
194                 if (rInfo.serviceInfo.metaData != null) {
195                     version = rInfo.serviceInfo.metaData.getInt(
196                             EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
197                     isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
198                 }
199 
200                 if (version > mVersion) {
201                     bestVersion = version;
202                     bestPackage = packageName;
203                     bestIsMultiuser = isMultiuser;
204                 }
205             }
206 
207             if (D) {
208                 Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
209                         (justCheckThisPackage == null ? ""
210                                 : "(" + justCheckThisPackage + ") "), rInfos.size(),
211                         (bestPackage == null ? "no new best package"
212                                 : "new best package: " + bestPackage)));
213             }
214         } else {
215             if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
216         }
217         if (bestPackage != null) {
218             bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
219             return true;
220         }
221         return false;
222     }
223 
unbindLocked()224     private void unbindLocked() {
225         String pkg;
226         pkg = mPackageName;
227         mPackageName = null;
228         mVersion = Integer.MIN_VALUE;
229         mIsMultiuser = false;
230         if (pkg != null) {
231             if (D) Log.d(mTag, "unbinding " + pkg);
232             mContext.unbindService(this);
233         }
234     }
235 
bindToPackageLocked(String packageName, int version, boolean isMultiuser)236     private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) {
237         unbindLocked();
238         Intent intent = new Intent(mAction);
239         intent.setPackage(packageName);
240         mPackageName = packageName;
241         mVersion = version;
242         mIsMultiuser = isMultiuser;
243         if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") ("
244                 + (isMultiuser ? "multi" : "single") + "-user)");
245         mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
246                 | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.OWNER : UserHandle.CURRENT);
247     }
248 
isSignatureMatch(Signature[] signatures, List<HashSet<Signature>> sigSets)249     public static boolean isSignatureMatch(Signature[] signatures,
250             List<HashSet<Signature>> sigSets) {
251         if (signatures == null) return false;
252 
253         // build hashset of input to test against
254         HashSet<Signature> inputSet = new HashSet<Signature>();
255         for (Signature s : signatures) {
256             inputSet.add(s);
257         }
258 
259         // test input against each of the signature sets
260         for (HashSet<Signature> referenceSet : sigSets) {
261             if (referenceSet.equals(inputSet)) {
262                 return true;
263             }
264         }
265         return false;
266     }
267 
isSignatureMatch(Signature[] signatures)268     private boolean isSignatureMatch(Signature[] signatures) {
269         return isSignatureMatch(signatures, mSignatureSets);
270     }
271 
272     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
273         /**
274          * Called when package has been reinstalled
275          */
276         @Override
277         public void onPackageUpdateFinished(String packageName, int uid) {
278             synchronized (mLock) {
279                 if (packageName.equals(mPackageName)) {
280                     // package updated, make sure to rebind
281                     unbindLocked();
282                 }
283                 // Need to check all packages because this method is also called when a
284                 // system app is uninstalled and the stock version in reinstalled.
285                 bindBestPackageLocked(null);
286             }
287         }
288 
289         @Override
290         public void onPackageAdded(String packageName, int uid) {
291             synchronized (mLock) {
292                 if (packageName.equals(mPackageName)) {
293                     // package updated, make sure to rebind
294                     unbindLocked();
295                 }
296                 // check the new package is case it is better
297                 bindBestPackageLocked(null);
298             }
299         }
300 
301         @Override
302         public void onPackageRemoved(String packageName, int uid) {
303             synchronized (mLock) {
304                 if (packageName.equals(mPackageName)) {
305                     unbindLocked();
306                     // the currently bound package was removed,
307                     // need to search for a new package
308                     bindBestPackageLocked(null);
309                 }
310             }
311         }
312 
313         @Override
314         public boolean onPackageChanged(String packageName, int uid, String[] components) {
315             synchronized (mLock) {
316                 if (packageName.equals(mPackageName)) {
317                     // service enabled or disabled, make sure to rebind
318                     unbindLocked();
319                 }
320                 // the service might be disabled, need to search for a new
321                 // package
322                 bindBestPackageLocked(null);
323             }
324             return super.onPackageChanged(packageName, uid, components);
325         }
326     };
327 
328     @Override
onServiceConnected(ComponentName name, IBinder binder)329     public void onServiceConnected(ComponentName name, IBinder binder) {
330         synchronized (mLock) {
331             String packageName = name.getPackageName();
332             if (packageName.equals(mPackageName)) {
333                 if (D) Log.d(mTag, packageName + " connected");
334                 mBinder = binder;
335                 if (mHandler !=null && mNewServiceWork != null) {
336                     mHandler.post(mNewServiceWork);
337                 }
338             } else {
339                 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
340             }
341         }
342     }
343 
344     @Override
onServiceDisconnected(ComponentName name)345     public void onServiceDisconnected(ComponentName name) {
346         synchronized (mLock) {
347             String packageName = name.getPackageName();
348             if (D) Log.d(mTag, packageName + " disconnected");
349 
350             if (packageName.equals(mPackageName)) {
351                 mBinder = null;
352             }
353         }
354     }
355 
getBestPackageName()356     public String getBestPackageName() {
357         synchronized (mLock) {
358             return mPackageName;
359         }
360     }
361 
getBestVersion()362     public int getBestVersion() {
363         synchronized (mLock) {
364             return mVersion;
365         }
366     }
367 
getBinder()368     public IBinder getBinder() {
369         synchronized (mLock) {
370             return mBinder;
371         }
372     }
373 
switchUser()374     public void switchUser() {
375         synchronized (mLock) {
376             if (!mIsMultiuser) {
377                 unbindLocked();
378                 bindBestPackageLocked(mServicePackageName);
379             }
380         }
381     }
382 }
383