1 /*
2  * Copyright (C) 2013 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.firewall;
18 
19 import android.annotation.NonNull;
20 import android.app.AppGlobals;
21 import android.content.ComponentName;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.IPackageManager;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManagerInternal;
28 import android.os.Binder;
29 import android.os.Environment;
30 import android.os.FileObserver;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.RemoteException;
35 import android.util.ArrayMap;
36 import android.util.Slog;
37 import android.util.Xml;
38 
39 import com.android.internal.util.ArrayUtils;
40 import com.android.internal.util.XmlUtils;
41 import com.android.server.EventLogTags;
42 import com.android.server.IntentResolver;
43 import com.android.server.LocalServices;
44 import com.android.server.pm.Computer;
45 
46 import org.xmlpull.v1.XmlPullParser;
47 import org.xmlpull.v1.XmlPullParserException;
48 
49 import java.io.File;
50 import java.io.FileInputStream;
51 import java.io.FileNotFoundException;
52 import java.io.IOException;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.HashMap;
56 import java.util.List;
57 
58 public class IntentFirewall {
59     static final String TAG = "IntentFirewall";
60 
61     // e.g. /data/system/ifw or /data/secure/system/ifw
62     private static final File RULES_DIR = new File(Environment.getDataSystemDirectory(), "ifw");
63 
64     private static final int LOG_PACKAGES_MAX_LENGTH = 150;
65     private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125;
66 
67     private static final String TAG_RULES = "rules";
68     private static final String TAG_ACTIVITY = "activity";
69     private static final String TAG_SERVICE = "service";
70     private static final String TAG_BROADCAST = "broadcast";
71 
72     private static final int TYPE_ACTIVITY = 0;
73     private static final int TYPE_BROADCAST = 1;
74     private static final int TYPE_SERVICE = 2;
75 
76     private static final HashMap<String, FilterFactory> factoryMap;
77 
78     private final AMSInterface mAms;
79 
80     private final RuleObserver mObserver;
81 
82     @NonNull
83     private PackageManagerInternal mPackageManager;
84 
85     private FirewallIntentResolver mActivityResolver = new FirewallIntentResolver();
86     private FirewallIntentResolver mBroadcastResolver = new FirewallIntentResolver();
87     private FirewallIntentResolver mServiceResolver = new FirewallIntentResolver();
88 
89     static {
90         FilterFactory[] factories = new FilterFactory[] {
91                 AndFilter.FACTORY,
92                 OrFilter.FACTORY,
93                 NotFilter.FACTORY,
94 
95                 StringFilter.ACTION,
96                 StringFilter.COMPONENT,
97                 StringFilter.COMPONENT_NAME,
98                 StringFilter.COMPONENT_PACKAGE,
99                 StringFilter.DATA,
100                 StringFilter.HOST,
101                 StringFilter.MIME_TYPE,
102                 StringFilter.SCHEME,
103                 StringFilter.PATH,
104                 StringFilter.SSP,
105 
106                 CategoryFilter.FACTORY,
107                 SenderFilter.FACTORY,
108                 SenderPackageFilter.FACTORY,
109                 SenderPermissionFilter.FACTORY,
110                 PortFilter.FACTORY
111         };
112 
113         // load factor ~= .75
114         factoryMap = new HashMap<String, FilterFactory>(factories.length * 4 / 3);
115         for (int i=0; i<factories.length; i++) {
116             FilterFactory factory = factories[i];
factory.getTagName()117             factoryMap.put(factory.getTagName(), factory);
118         }
119     }
120 
IntentFirewall(AMSInterface ams, Handler handler)121     public IntentFirewall(AMSInterface ams, Handler handler) {
122         mAms = ams;
123         mHandler = new FirewallHandler(handler.getLooper());
124         File rulesDir = getRulesDir();
125         rulesDir.mkdirs();
126 
127         readRulesDir(rulesDir);
128 
129         mObserver = new RuleObserver(rulesDir);
130         mObserver.startWatching();
131     }
132 
getPackageManager()133     PackageManagerInternal getPackageManager() {
134         if (mPackageManager == null) {
135             mPackageManager = LocalServices.getService(PackageManagerInternal.class);
136         }
137         return mPackageManager;
138     }
139 
140     /**
141      * This is called from ActivityManager to check if a start activity intent should be allowed.
142      * It is assumed the caller is already holding the global ActivityManagerService lock.
143      */
checkStartActivity(Intent intent, int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp)144     public boolean checkStartActivity(Intent intent, int callerUid, int callerPid,
145             String resolvedType, ApplicationInfo resolvedApp) {
146         return checkIntent(mActivityResolver, intent.getComponent(), TYPE_ACTIVITY, intent,
147                 callerUid, callerPid, resolvedType, resolvedApp.uid);
148     }
149 
checkService(ComponentName resolvedService, Intent intent, int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp)150     public boolean checkService(ComponentName resolvedService, Intent intent, int callerUid,
151             int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
152         return checkIntent(mServiceResolver, resolvedService, TYPE_SERVICE, intent, callerUid,
153                 callerPid, resolvedType, resolvedApp.uid);
154     }
155 
checkBroadcast(Intent intent, int callerUid, int callerPid, String resolvedType, int receivingUid)156     public boolean checkBroadcast(Intent intent, int callerUid, int callerPid,
157             String resolvedType, int receivingUid) {
158         return checkIntent(mBroadcastResolver, intent.getComponent(), TYPE_BROADCAST, intent,
159                 callerUid, callerPid, resolvedType, receivingUid);
160     }
161 
checkIntent(FirewallIntentResolver resolver, ComponentName resolvedComponent, int intentType, Intent intent, int callerUid, int callerPid, String resolvedType, int receivingUid)162     public boolean checkIntent(FirewallIntentResolver resolver, ComponentName resolvedComponent,
163             int intentType, Intent intent, int callerUid, int callerPid, String resolvedType,
164             int receivingUid) {
165         boolean log = false;
166         boolean block = false;
167 
168         // For the first pass, find all the rules that have at least one intent-filter or
169         // component-filter that matches this intent
170         List<Rule> candidateRules;
171         candidateRules = resolver.queryIntent(getPackageManager().snapshot(), intent, resolvedType,
172                 false /*defaultOnly*/, 0);
173         if (candidateRules == null) {
174             candidateRules = new ArrayList<Rule>();
175         }
176         resolver.queryByComponent(resolvedComponent, candidateRules);
177 
178         // For the second pass, try to match the potentially more specific conditions in each
179         // rule against the intent
180         for (int i=0; i<candidateRules.size(); i++) {
181             Rule rule = candidateRules.get(i);
182             if (rule.matches(this, resolvedComponent, intent, callerUid, callerPid, resolvedType,
183                     receivingUid)) {
184                 block |= rule.getBlock();
185                 log |= rule.getLog();
186 
187                 // if we've already determined that we should both block and log, there's no need
188                 // to continue trying rules
189                 if (block && log) {
190                     break;
191                 }
192             }
193         }
194 
195         if (log) {
196             logIntent(intentType, intent, callerUid, resolvedType);
197         }
198 
199         return !block;
200     }
201 
logIntent(int intentType, Intent intent, int callerUid, String resolvedType)202     private static void logIntent(int intentType, Intent intent, int callerUid,
203             String resolvedType) {
204         // The component shouldn't be null, but let's double check just to be safe
205         ComponentName cn = intent.getComponent();
206         String shortComponent = null;
207         if (cn != null) {
208             shortComponent = cn.flattenToShortString();
209         }
210 
211         String callerPackages = null;
212         int callerPackageCount = 0;
213         IPackageManager pm = AppGlobals.getPackageManager();
214         if (pm != null) {
215             try {
216                 String[] callerPackagesArray = pm.getPackagesForUid(callerUid);
217                 if (callerPackagesArray != null) {
218                     callerPackageCount = callerPackagesArray.length;
219                     callerPackages = joinPackages(callerPackagesArray);
220                 }
221             } catch (RemoteException ex) {
222                 Slog.e(TAG, "Remote exception while retrieving packages", ex);
223             }
224         }
225 
226         EventLogTags.writeIfwIntentMatched(intentType, shortComponent, callerUid,
227                 callerPackageCount, callerPackages, intent.getAction(), resolvedType,
228                 intent.getDataString(), intent.getFlags());
229     }
230 
231     /**
232      * Joins a list of package names such that the resulting string is no more than
233      * LOG_PACKAGES_MAX_LENGTH.
234      *
235      * Only full package names will be added to the result, unless every package is longer than the
236      * limit, in which case one of the packages will be truncated and added. In this case, an
237      * additional '-' character will be added to the end of the string, to denote the truncation.
238      *
239      * If it encounters a package that won't fit in the remaining space, it will continue on to the
240      * next package, unless the total length of the built string so far is greater than
241      * LOG_PACKAGES_SUFFICIENT_LENGTH, in which case it will stop and return what it has.
242      */
joinPackages(String[] packages)243     private static String joinPackages(String[] packages) {
244         boolean first = true;
245         StringBuilder sb = new StringBuilder();
246         for (int i=0; i<packages.length; i++) {
247             String pkg = packages[i];
248 
249             // + 1 length for the comma. This logic technically isn't correct for the first entry,
250             // but it's not critical.
251             if (sb.length() + pkg.length() + 1 < LOG_PACKAGES_MAX_LENGTH) {
252                 if (!first) {
253                     sb.append(',');
254                 } else {
255                     first = false;
256                 }
257                 sb.append(pkg);
258             } else if (sb.length() >= LOG_PACKAGES_SUFFICIENT_LENGTH) {
259                 return sb.toString();
260             }
261         }
262         if (sb.length() == 0 && packages.length > 0) {
263             String pkg = packages[0];
264             // truncating from the end - the last part of the package name is more likely to be
265             // interesting/unique
266             return pkg.substring(pkg.length() - LOG_PACKAGES_MAX_LENGTH + 1) + '-';
267         }
268         return null;
269     }
270 
getRulesDir()271     public static File getRulesDir() {
272         return RULES_DIR;
273     }
274 
275     /**
276      * Reads rules from all xml files (*.xml) in the given directory, and replaces our set of rules
277      * with the newly read rules.
278      *
279      * We only check for files ending in ".xml", to allow for temporary files that are atomically
280      * renamed to .xml
281      *
282      * All calls to this method from the file observer come through a handler and are inherently
283      * serialized
284      */
readRulesDir(File rulesDir)285     private void readRulesDir(File rulesDir) {
286         FirewallIntentResolver[] resolvers = new FirewallIntentResolver[3];
287         for (int i=0; i<resolvers.length; i++) {
288             resolvers[i] = new FirewallIntentResolver();
289         }
290 
291         File[] files = rulesDir.listFiles();
292         if (files != null) {
293             for (int i=0; i<files.length; i++) {
294                 File file = files[i];
295 
296                 if (file.getName().endsWith(".xml")) {
297                     readRules(file, resolvers);
298                 }
299             }
300         }
301 
302         Slog.i(TAG, "Read new rules (A:" + resolvers[TYPE_ACTIVITY].filterSet().size() +
303                 " B:" + resolvers[TYPE_BROADCAST].filterSet().size() +
304                 " S:" + resolvers[TYPE_SERVICE].filterSet().size() + ")");
305 
306         synchronized (mAms.getAMSLock()) {
307             mActivityResolver = resolvers[TYPE_ACTIVITY];
308             mBroadcastResolver = resolvers[TYPE_BROADCAST];
309             mServiceResolver = resolvers[TYPE_SERVICE];
310         }
311     }
312 
313     /**
314      * Reads rules from the given file and add them to the given resolvers
315      */
readRules(File rulesFile, FirewallIntentResolver[] resolvers)316     private void readRules(File rulesFile, FirewallIntentResolver[] resolvers) {
317         // some temporary lists to hold the rules while we parse the xml file, so that we can
318         // add the rules all at once, after we know there weren't any major structural problems
319         // with the xml file
320         List<List<Rule>> rulesByType = new ArrayList<List<Rule>>(3);
321         for (int i=0; i<3; i++) {
322             rulesByType.add(new ArrayList<Rule>());
323         }
324 
325         FileInputStream fis;
326         try {
327             fis = new FileInputStream(rulesFile);
328         } catch (FileNotFoundException ex) {
329             // Nope, no rules. Nothing else to do!
330             return;
331         }
332 
333         try {
334             XmlPullParser parser = Xml.newPullParser();
335 
336             parser.setInput(fis, null);
337 
338             XmlUtils.beginDocument(parser, TAG_RULES);
339 
340             int outerDepth = parser.getDepth();
341             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
342                 int ruleType = -1;
343 
344                 String tagName = parser.getName();
345                 if (tagName.equals(TAG_ACTIVITY)) {
346                     ruleType = TYPE_ACTIVITY;
347                 } else if (tagName.equals(TAG_BROADCAST)) {
348                     ruleType = TYPE_BROADCAST;
349                 } else if (tagName.equals(TAG_SERVICE)) {
350                     ruleType = TYPE_SERVICE;
351                 }
352 
353                 if (ruleType != -1) {
354                     Rule rule = new Rule();
355 
356                     List<Rule> rules = rulesByType.get(ruleType);
357 
358                     // if we get an error while parsing a particular rule, we'll just ignore
359                     // that rule and continue on with the next rule
360                     try {
361                         rule.readFromXml(parser);
362                     } catch (XmlPullParserException ex) {
363                         Slog.e(TAG, "Error reading an intent firewall rule from " + rulesFile, ex);
364                         continue;
365                     }
366 
367                     rules.add(rule);
368                 }
369             }
370         } catch (XmlPullParserException ex) {
371             // if there was an error outside of a specific rule, then there are probably
372             // structural problems with the xml file, and we should completely ignore it
373             Slog.e(TAG, "Error reading intent firewall rules from " + rulesFile, ex);
374             return;
375         } catch (IOException ex) {
376             Slog.e(TAG, "Error reading intent firewall rules from " + rulesFile, ex);
377             return;
378         } finally {
379             try {
380                 fis.close();
381             } catch (IOException ex) {
382                 Slog.e(TAG, "Error while closing " + rulesFile, ex);
383             }
384         }
385 
386         for (int ruleType=0; ruleType<rulesByType.size(); ruleType++) {
387             List<Rule> rules = rulesByType.get(ruleType);
388             FirewallIntentResolver resolver = resolvers[ruleType];
389 
390             for (int ruleIndex=0; ruleIndex<rules.size(); ruleIndex++) {
391                 Rule rule = rules.get(ruleIndex);
392                 for (int i=0; i<rule.getIntentFilterCount(); i++) {
393                     resolver.addFilter(null, rule.getIntentFilter(i));
394                 }
395                 for (int i=0; i<rule.getComponentFilterCount(); i++) {
396                     resolver.addComponentFilter(rule.getComponentFilter(i), rule);
397                 }
398             }
399         }
400     }
401 
parseFilter(XmlPullParser parser)402     static Filter parseFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
403         String elementName = parser.getName();
404 
405         FilterFactory factory = factoryMap.get(elementName);
406 
407         if (factory == null) {
408             throw new XmlPullParserException("Unknown element in filter list: " + elementName);
409         }
410         return factory.newFilter(parser);
411     }
412 
413     /**
414      * Represents a single activity/service/broadcast rule within one of the xml files.
415      *
416      * Rules are matched against an incoming intent in two phases. The goal of the first phase
417      * is to select a subset of rules that might match a given intent.
418      *
419      * For the first phase, we use a combination of intent filters (via an IntentResolver)
420      * and component filters to select which rules to check. If a rule has multiple intent or
421      * component filters, only a single filter must match for the rule to be passed on to the
422      * second phase.
423      *
424      * In the second phase, we check the specific conditions in each rule against the values in the
425      * intent. All top level conditions (but not filters) in the rule must match for the rule as a
426      * whole to match.
427      *
428      * If the rule matches, then we block or log the intent, as specified by the rule. If multiple
429      * rules match, we combine the block/log flags from any matching rule.
430      */
431     private static class Rule extends AndFilter {
432         private static final String TAG_INTENT_FILTER = "intent-filter";
433         private static final String TAG_COMPONENT_FILTER = "component-filter";
434         private static final String ATTR_NAME = "name";
435 
436         private static final String ATTR_BLOCK = "block";
437         private static final String ATTR_LOG = "log";
438 
439         private final ArrayList<FirewallIntentFilter> mIntentFilters =
440                 new ArrayList<FirewallIntentFilter>(1);
441         private final ArrayList<ComponentName> mComponentFilters = new ArrayList<ComponentName>(0);
442         private boolean block;
443         private boolean log;
444 
445         @Override
readFromXml(XmlPullParser parser)446         public Rule readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
447             block = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_BLOCK));
448             log = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_LOG));
449 
450             super.readFromXml(parser);
451             return this;
452         }
453 
454         @Override
readChild(XmlPullParser parser)455         protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
456             String currentTag = parser.getName();
457 
458             if (currentTag.equals(TAG_INTENT_FILTER)) {
459                 FirewallIntentFilter intentFilter = new FirewallIntentFilter(this);
460                 intentFilter.readFromXml(parser);
461                 mIntentFilters.add(intentFilter);
462             } else if (currentTag.equals(TAG_COMPONENT_FILTER)) {
463                 String componentStr = parser.getAttributeValue(null, ATTR_NAME);
464                 if (componentStr == null) {
465                     throw new XmlPullParserException("Component name must be specified.",
466                             parser, null);
467                 }
468 
469                 ComponentName componentName = ComponentName.unflattenFromString(componentStr);
470                 if (componentName == null) {
471                     throw new XmlPullParserException("Invalid component name: " + componentStr);
472                 }
473 
474                 mComponentFilters.add(componentName);
475             } else {
476                 super.readChild(parser);
477             }
478         }
479 
getIntentFilterCount()480         public int getIntentFilterCount() {
481             return mIntentFilters.size();
482         }
483 
getIntentFilter(int index)484         public FirewallIntentFilter getIntentFilter(int index) {
485             return mIntentFilters.get(index);
486         }
487 
getComponentFilterCount()488         public int getComponentFilterCount() {
489             return mComponentFilters.size();
490         }
491 
getComponentFilter(int index)492         public ComponentName getComponentFilter(int index) {
493             return mComponentFilters.get(index);
494         }
getBlock()495         public boolean getBlock() {
496             return block;
497         }
498 
getLog()499         public boolean getLog() {
500             return log;
501         }
502     }
503 
504     private static class FirewallIntentFilter extends IntentFilter {
505         private final Rule rule;
506 
FirewallIntentFilter(Rule rule)507         public FirewallIntentFilter(Rule rule) {
508             this.rule = rule;
509         }
510     }
511 
512     private static class FirewallIntentResolver
513             extends IntentResolver<FirewallIntentFilter, Rule> {
514         @Override
allowFilterResult(FirewallIntentFilter filter, List<Rule> dest)515         protected boolean allowFilterResult(FirewallIntentFilter filter, List<Rule> dest) {
516             return !dest.contains(filter.rule);
517         }
518 
519         @Override
isPackageForFilter(String packageName, FirewallIntentFilter filter)520         protected boolean isPackageForFilter(String packageName, FirewallIntentFilter filter) {
521             return true;
522         }
523 
524         @Override
newArray(int size)525         protected FirewallIntentFilter[] newArray(int size) {
526             return new FirewallIntentFilter[size];
527         }
528 
529         @Override
newResult(@onNull Computer computer, FirewallIntentFilter filter, int match, int userId, long customFlags)530         protected Rule newResult(@NonNull Computer computer, FirewallIntentFilter filter,
531                 int match, int userId, long customFlags) {
532             return filter.rule;
533         }
534 
535         @Override
sortResults(List<Rule> results)536         protected void sortResults(List<Rule> results) {
537             // there's no need to sort the results
538             return;
539         }
540 
541         @Override
getIntentFilter(@onNull FirewallIntentFilter input)542         protected IntentFilter getIntentFilter(@NonNull FirewallIntentFilter input) {
543             return input;
544         }
545 
queryByComponent(ComponentName componentName, List<Rule> candidateRules)546         public void queryByComponent(ComponentName componentName, List<Rule> candidateRules) {
547             Rule[] rules = mRulesByComponent.get(componentName);
548             if (rules != null) {
549                 candidateRules.addAll(Arrays.asList(rules));
550             }
551         }
552 
addComponentFilter(ComponentName componentName, Rule rule)553         public void addComponentFilter(ComponentName componentName, Rule rule) {
554             Rule[] rules = mRulesByComponent.get(componentName);
555             rules = ArrayUtils.appendElement(Rule.class, rules, rule);
556             mRulesByComponent.put(componentName, rules);
557         }
558 
559         private final ArrayMap<ComponentName, Rule[]> mRulesByComponent =
560                 new ArrayMap<ComponentName, Rule[]>(0);
561     }
562 
563     final FirewallHandler mHandler;
564 
565     private final class FirewallHandler extends Handler {
FirewallHandler(Looper looper)566         public FirewallHandler(Looper looper) {
567             super(looper, null, true);
568         }
569 
570         @Override
handleMessage(Message msg)571         public void handleMessage(Message msg) {
572             readRulesDir(getRulesDir());
573         }
574     };
575 
576     /**
577      * Monitors for the creation/deletion/modification of any .xml files in the rule directory
578      */
579     private class RuleObserver extends FileObserver {
580         private static final int MONITORED_EVENTS = FileObserver.CREATE|FileObserver.MOVED_TO|
581                 FileObserver.CLOSE_WRITE|FileObserver.DELETE|FileObserver.MOVED_FROM;
582 
RuleObserver(File monitoredDir)583         public RuleObserver(File monitoredDir) {
584             super(monitoredDir.getAbsolutePath(), MONITORED_EVENTS);
585         }
586 
587         @Override
onEvent(int event, String path)588         public void onEvent(int event, String path) {
589             if (path != null && path.endsWith(".xml")) {
590                 // we wait 250ms before taking any action on an event, in order to dedup multiple
591                 // events. E.g. a delete event followed by a create event followed by a subsequent
592                 // write+close event
593                 mHandler.removeMessages(0);
594                 mHandler.sendEmptyMessageDelayed(0, 250);
595             }
596         }
597     }
598 
599     /**
600      * This interface contains the methods we need from ActivityManagerService. This allows AMS to
601      * export these methods to us without making them public, and also makes it easier to test this
602      * component.
603      */
604     public interface AMSInterface {
checkComponentPermission(String permission, int pid, int uid, int owningUid, boolean exported)605         int checkComponentPermission(String permission, int pid, int uid,
606                 int owningUid, boolean exported);
getAMSLock()607         Object getAMSLock();
608     }
609 
610     /**
611      * Checks if the caller has access to a component
612      *
613      * @param permission If present, the caller must have this permission
614      * @param pid The pid of the caller
615      * @param uid The uid of the caller
616      * @param owningUid The uid of the application that owns the component
617      * @param exported Whether the component is exported
618      * @return True if the caller can access the described component
619      */
checkComponentPermission(String permission, int pid, int uid, int owningUid, boolean exported)620     boolean checkComponentPermission(String permission, int pid, int uid, int owningUid,
621             boolean exported) {
622         return mAms.checkComponentPermission(permission, pid, uid, owningUid, exported) ==
623                 PackageManager.PERMISSION_GRANTED;
624     }
625 
signaturesMatch(int uid1, int uid2)626     boolean signaturesMatch(int uid1, int uid2) {
627         final long token = Binder.clearCallingIdentity();
628         try {
629             // Compare signatures of two packages for different users.
630             return getPackageManager()
631                     .checkUidSignaturesForAllUsers(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
632         } finally {
633             Binder.restoreCallingIdentity(token);
634         }
635     }
636 
637 }
638