OSDN Git Service

Rename bindService to bindServiceAsUser to follow convention.
[android-x86/frameworks-base.git] / services / java / com / android / server / ServiceWatcher.java
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.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.content.pm.ResolveInfo;
27 import android.content.pm.Signature;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.UserHandle;
31 import android.util.Log;
32
33 import com.android.internal.content.PackageMonitor;
34
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.HashSet;
38 import java.util.List;
39
40 /**
41  * Find the best Service, and bind to it.
42  * Handles run-time package changes.
43  */
44 public class ServiceWatcher implements ServiceConnection {
45     private static final boolean D = false;
46     public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
47
48     private final String mTag;
49     private final Context mContext;
50     private final PackageManager mPm;
51     private final List<HashSet<Signature>> mSignatureSets;
52     private final String mAction;
53     private final Runnable mNewServiceWork;
54     private final Handler mHandler;
55
56     private Object mLock = new Object();
57
58     // all fields below synchronized on mLock
59     private IBinder mBinder;   // connected service
60     private String mPackageName;  // current best package
61     private int mVersion = Integer.MIN_VALUE;  // current best version
62     private int mCurrentUserId;
63
64     public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
65             List<String> initialPackageNames) {
66         PackageManager pm = context.getPackageManager();
67         ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
68         for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
69             String pkg = initialPackageNames.get(i);
70             try {
71                 HashSet<Signature> set = new HashSet<Signature>();
72                 Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
73                 set.addAll(Arrays.asList(sigs));
74                 sigSets.add(set);
75             } catch (NameNotFoundException e) {
76                 Log.w("ServiceWatcher", pkg + " not found");
77             }
78         }
79         return sigSets;
80     }
81
82     public ServiceWatcher(Context context, String logTag, String action,
83             List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
84         mContext = context;
85         mTag = logTag;
86         mAction = action;
87         mPm = mContext.getPackageManager();
88         mNewServiceWork = newServiceWork;
89         mHandler = handler;
90         mCurrentUserId = userId;
91
92         mSignatureSets = getSignatureSets(context, initialPackageNames);
93     }
94
95     public boolean start() {
96         synchronized (mLock) {
97             if (!bindBestPackageLocked(null)) return false;
98         }
99
100         mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
101         return true;
102     }
103
104     /**
105      * Searches and binds to the best package, or do nothing
106      * if the best package is already bound.
107      * Only checks the named package, or checks all packages if it
108      * is null.
109      * Return true if a new package was found to bind to.
110      */
111     private boolean bindBestPackageLocked(String justCheckThisPackage) {
112         Intent intent = new Intent(mAction);
113         if (justCheckThisPackage != null) {
114             intent.setPackage(justCheckThisPackage);
115         }
116         List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
117                 PackageManager.GET_META_DATA, mCurrentUserId);
118         int bestVersion = Integer.MIN_VALUE;
119         String bestPackage = null;
120         for (ResolveInfo rInfo : rInfos) {
121             String packageName = rInfo.serviceInfo.packageName;
122
123             // check signature
124             try {
125                 PackageInfo pInfo;
126                 pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
127                 if (!isSignatureMatch(pInfo.signatures)) {
128                     Log.w(mTag, packageName + " resolves service " + mAction +
129                             ", but has wrong signature, ignoring");
130                     continue;
131                 }
132             } catch (NameNotFoundException e) {
133                 Log.wtf(mTag, e);
134                 continue;
135             }
136
137             // check version
138             int version = 0;
139             if (rInfo.serviceInfo.metaData != null) {
140                 version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, 0);
141             }
142
143             if (version > mVersion) {
144                 bestVersion = version;
145                 bestPackage = packageName;
146             }
147         }
148
149         if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
150                 (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
151                 rInfos.size(),
152                 (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
153
154         if (bestPackage != null) {
155             bindToPackageLocked(bestPackage, bestVersion);
156             return true;
157         }
158         return false;
159     }
160
161     private void unbindLocked() {
162         String pkg;
163         pkg = mPackageName;
164         mPackageName = null;
165         mVersion = Integer.MIN_VALUE;
166         if (pkg != null) {
167             if (D) Log.d(mTag, "unbinding " + pkg);
168             mContext.unbindService(this);
169         }
170     }
171
172     private void bindToPackageLocked(String packageName, int version) {
173         unbindLocked();
174         Intent intent = new Intent(mAction);
175         intent.setPackage(packageName);
176         mPackageName = packageName;
177         mVersion = version;
178         if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
179         mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
180                 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE,
181                 new UserHandle(mCurrentUserId));
182     }
183
184     public static boolean isSignatureMatch(Signature[] signatures,
185             List<HashSet<Signature>> sigSets) {
186         if (signatures == null) return false;
187
188         // build hashset of input to test against
189         HashSet<Signature> inputSet = new HashSet<Signature>();
190         for (Signature s : signatures) {
191             inputSet.add(s);
192         }
193
194         // test input against each of the signature sets
195         for (HashSet<Signature> referenceSet : sigSets) {
196             if (referenceSet.equals(inputSet)) {
197                 return true;
198             }
199         }
200         return false;
201     }
202
203     private boolean isSignatureMatch(Signature[] signatures) {
204         return isSignatureMatch(signatures, mSignatureSets);
205     }
206
207     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
208         /**
209          * Called when package has been reinstalled
210          */
211         @Override
212         public void onPackageUpdateFinished(String packageName, int uid) {
213             synchronized (mLock) {
214                 if (packageName.equals(mPackageName)) {
215                     // package updated, make sure to rebind
216                     unbindLocked();
217                 }
218                 // check the updated package in case it is better
219                 bindBestPackageLocked(packageName);
220             }
221         }
222
223         @Override
224         public void onPackageAdded(String packageName, int uid) {
225             synchronized (mLock) {
226                 if (packageName.equals(mPackageName)) {
227                     // package updated, make sure to rebind
228                     unbindLocked();
229                 }
230                 // check the new package is case it is better
231                 bindBestPackageLocked(packageName);
232             }
233         }
234
235         @Override
236         public void onPackageRemoved(String packageName, int uid) {
237             synchronized (mLock) {
238                 if (packageName.equals(mPackageName)) {
239                     unbindLocked();
240                     // the currently bound package was removed,
241                     // need to search for a new package
242                     bindBestPackageLocked(null);
243                 }
244             }
245         }
246     };
247
248     @Override
249     public void onServiceConnected(ComponentName name, IBinder binder) {
250         synchronized (mLock) {
251             String packageName = name.getPackageName();
252             if (packageName.equals(mPackageName)) {
253                 if (D) Log.d(mTag, packageName + " connected");
254                 mBinder = binder;
255                 if (mHandler !=null && mNewServiceWork != null) {
256                     mHandler.post(mNewServiceWork);
257                 }
258             } else {
259                 Log.w(mTag, "unexpected onServiceConnected: " + packageName);
260             }
261         }
262     }
263
264     @Override
265     public void onServiceDisconnected(ComponentName name) {
266         synchronized (mLock) {
267             String packageName = name.getPackageName();
268             if (D) Log.d(mTag, packageName + " disconnected");
269
270             if (packageName.equals(mPackageName)) {
271                 mBinder = null;
272             }
273         }
274     }
275
276     public String getBestPackageName() {
277         synchronized (mLock) {
278             return mPackageName;
279         }
280     }
281
282     public int getBestVersion() {
283         synchronized (mLock) {
284             return mVersion;
285         }
286     }
287
288     public IBinder getBinder() {
289         synchronized (mLock) {
290             return mBinder;
291         }
292     }
293
294     public void switchUser(int userId) {
295         synchronized (mLock) {
296             unbindLocked();
297             mCurrentUserId = userId;
298             bindBestPackageLocked(null);
299         }
300     }
301 }