1 /*
2  * Copyright (C) 2021 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.pm;
18 
19 import static android.content.Intent.CATEGORY_DEFAULT;
20 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
21 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
22 
23 import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
24 import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
25 import static com.android.server.pm.PackageManagerService.TAG;
26 
27 import android.annotation.NonNull;
28 import android.annotation.UserIdInt;
29 import android.content.ComponentName;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.ResolveInfo;
35 import android.os.Binder;
36 import android.os.Build;
37 import android.os.Process;
38 import android.os.UserHandle;
39 import android.text.TextUtils;
40 import android.util.EventLog;
41 import android.util.Log;
42 import android.util.LogPrinter;
43 import android.util.PrintStreamPrinter;
44 import android.util.Slog;
45 import android.util.SparseBooleanArray;
46 import android.util.Xml;
47 
48 import com.android.internal.util.ArrayUtils;
49 import com.android.modules.utils.TypedXmlPullParser;
50 import com.android.modules.utils.TypedXmlSerializer;
51 import com.android.server.net.NetworkPolicyManagerInternal;
52 import com.android.server.pm.pkg.PackageStateInternal;
53 
54 import org.xmlpull.v1.XmlPullParser;
55 import org.xmlpull.v1.XmlPullParserException;
56 
57 import java.io.ByteArrayInputStream;
58 import java.io.ByteArrayOutputStream;
59 import java.io.IOException;
60 import java.nio.charset.StandardCharsets;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Iterator;
64 import java.util.List;
65 
66 final class PreferredActivityHelper {
67     // XML tags for backup/restore of various bits of state
68     private static final String TAG_PREFERRED_BACKUP = "pa";
69     private static final String TAG_DEFAULT_APPS = "da";
70 
71     private final PackageManagerService mPm;
72 
73     // TODO(b/198166813): remove PMS dependency
PreferredActivityHelper(PackageManagerService pm)74     PreferredActivityHelper(PackageManagerService pm) {
75         mPm = pm;
76     }
77 
findPreferredActivityNotLocked(@onNull Computer snapshot, Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug, @UserIdInt int userId)78     private ResolveInfo findPreferredActivityNotLocked(@NonNull Computer snapshot, Intent intent,
79             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
80             List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
81             @UserIdInt int userId) {
82         return findPreferredActivityNotLocked(snapshot, intent, resolvedType, flags, query, always,
83                 removeMatches, debug, userId,
84                 UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
85     }
86 
87     // TODO: handle preferred activities missing while user has amnesia
88     /** <b>must not hold {@link PackageManagerService.mLock}</b> */
findPreferredActivityNotLocked(@onNull Computer snapshot, Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered)89     public ResolveInfo findPreferredActivityNotLocked(@NonNull Computer snapshot,
90             Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
91             List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
92             int userId, boolean queryMayBeFiltered) {
93         if (Thread.holdsLock(mPm.mLock)) {
94             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
95                     + " is holding mLock", new Throwable());
96         }
97         if (!mPm.mUserManager.exists(userId)) return null;
98 
99         PackageManagerService.FindPreferredActivityBodyResult body =
100                 snapshot.findPreferredActivityInternal(
101                 intent, resolvedType, flags, query, always,
102                 removeMatches, debug, userId, queryMayBeFiltered);
103         if (body.mChanged) {
104             if (DEBUG_PREFERRED) {
105                 Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
106             }
107             mPm.scheduleWritePackageRestrictions(userId);
108         }
109         if ((DEBUG_PREFERRED || debug) && body.mPreferredResolveInfo == null) {
110             Slog.v(TAG, "No preferred activity to return");
111         }
112         return body.mPreferredResolveInfo;
113     }
114 
115     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
clearPackagePreferredActivities(String packageName, int userId)116     public void clearPackagePreferredActivities(String packageName, int userId) {
117         final SparseBooleanArray changedUsers = new SparseBooleanArray();
118         synchronized (mPm.mLock) {
119             mPm.clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
120         }
121         if (changedUsers.size() > 0) {
122             updateDefaultHomeNotLocked(mPm.snapshotComputer(), changedUsers);
123             mPm.postPreferredActivityChangedBroadcast(userId);
124             mPm.scheduleWritePackageRestrictions(userId);
125         }
126     }
127 
128     /**
129      * <b>must not hold {@link PackageManagerService.mLock}</b>
130      *
131      * @return Whether the ACTION_PREFERRED_ACTIVITY_CHANGED broadcast has been scheduled.
132      */
updateDefaultHomeNotLocked(@onNull Computer snapshot, @UserIdInt int userId)133     public boolean updateDefaultHomeNotLocked(@NonNull Computer snapshot, @UserIdInt int userId) {
134         if (Thread.holdsLock(mPm.mLock)) {
135             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
136                     + " is holding mLock", new Throwable());
137         }
138         if (!mPm.isSystemReady()) {
139             // We might get called before system is ready because of package changes etc, but
140             // finding preferred activity depends on settings provider, so we ignore the update
141             // before that.
142             return false;
143         }
144         final Intent intent = snapshot.getHomeIntent();
145         final List<ResolveInfo> resolveInfos = snapshot.queryIntentActivitiesInternal(
146                 intent, null, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
147         final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(snapshot,
148                 intent, null, 0, resolveInfos, true, false, false, userId);
149         final String packageName = preferredResolveInfo != null
150                 && preferredResolveInfo.activityInfo != null
151                 ? preferredResolveInfo.activityInfo.packageName : null;
152         final String currentPackageName = mPm.getActiveLauncherPackageName(userId);
153         if (TextUtils.equals(currentPackageName, packageName)) {
154             return false;
155         }
156         final String[] callingPackages = snapshot.getPackagesForUid(Binder.getCallingUid());
157         if (callingPackages != null && ArrayUtils.contains(callingPackages,
158                 mPm.mRequiredPermissionControllerPackage)) {
159             // PermissionController manages default home directly.
160             return false;
161         }
162 
163         if (packageName == null) {
164             // Keep the default home package in RoleManager.
165             return false;
166         }
167         return mPm.setActiveLauncherPackage(packageName, userId,
168                 successful -> {
169                     if (successful) {
170                         mPm.postPreferredActivityChangedBroadcast(userId);
171                     }
172                 });
173     }
174 
175     /**
176      * Variant that takes a {@link WatchedIntentFilter}
177      */
178     public void addPreferredActivity(@NonNull Computer snapshot, WatchedIntentFilter filter,
179             int match, ComponentName[] set, ComponentName activity, boolean always, int userId,
180             String opname, boolean removeExisting) {
181         // writer
182         int callingUid = Binder.getCallingUid();
183         snapshot.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
184                 false /* checkShell */, "add preferred activity");
185         if (mPm.mContext.checkCallingOrSelfPermission(
186                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
187                 != PackageManager.PERMISSION_GRANTED) {
188             if (snapshot.getUidTargetSdkVersion(callingUid)
189                     < Build.VERSION_CODES.FROYO) {
190                 Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
191                         + callingUid);
192                 return;
193             }
194             mPm.mContext.enforceCallingOrSelfPermission(
195                     android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
196         }
197         if (filter.countActions() == 0) {
198             Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
199             return;
200         }
201         if (DEBUG_PREFERRED) {
202             Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user "
203                     + userId + ":");
204             filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
205         }
206         synchronized (mPm.mLock) {
207             final PreferredIntentResolver pir = mPm.mSettings.editPreferredActivitiesLPw(userId);
208             final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
209             if (removeExisting && existing != null) {
210                 Settings.removeFilters(pir, filter, existing);
211             }
212             pir.addFilter(mPm.snapshotComputer(),
213                     new PreferredActivity(filter, match, set, activity, always));
214             mPm.scheduleWritePackageRestrictions(userId);
215         }
216         // Re-snapshot after mLock
217         if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId))) {
218             mPm.postPreferredActivityChangedBroadcast(userId);
219         }
220     }
221 
222     /**
223      * Variant that takes a {@link WatchedIntentFilter}
224      */
225     public void replacePreferredActivity(@NonNull Computer snapshot, WatchedIntentFilter filter,
226             int match, ComponentName[] set, ComponentName activity, int userId) {
227         if (filter.countActions() != 1) {
228             throw new IllegalArgumentException(
229                     "replacePreferredActivity expects filter to have only 1 action.");
230         }
231         if (filter.countDataAuthorities() != 0
232                 || filter.countDataPaths() != 0
233                 || filter.countDataSchemes() > 1
234                 || filter.countDataTypes() != 0) {
235             throw new IllegalArgumentException(
236                     "replacePreferredActivity expects filter to have no data authorities, "
237                             + "paths, or types; and at most one scheme.");
238         }
239 
240         final int callingUid = Binder.getCallingUid();
241         snapshot.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
242                 false /* checkShell */, "replace preferred activity");
243         if (mPm.mContext.checkCallingOrSelfPermission(
244                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
245                 != PackageManager.PERMISSION_GRANTED) {
246             synchronized (mPm.mLock) {
247                 // TODO: Remove lock?
248                 if (mPm.snapshotComputer().getUidTargetSdkVersion(callingUid)
249                         < Build.VERSION_CODES.FROYO) {
250                     Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
251                             + Binder.getCallingUid());
252                     return;
253                 }
254             }
255             mPm.mContext.enforceCallingOrSelfPermission(
256                     android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
257         }
258 
259         synchronized (mPm.mLock) {
260             final PreferredIntentResolver pir = mPm.mSettings.getPreferredActivities(userId);
261             if (pir != null) {
262                 // Get all of the existing entries that exactly match this filter.
263                 final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
264                 if (existing != null && existing.size() == 1) {
265                     final PreferredActivity cur = existing.get(0);
266                     if (DEBUG_PREFERRED) {
267                         Slog.i(TAG, "Checking replace of preferred:");
268                         filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
269                         if (!cur.mPref.mAlways) {
270                             Slog.i(TAG, "  -- CUR; not mAlways!");
271                         } else {
272                             Slog.i(TAG, "  -- CUR: mMatch=" + cur.mPref.mMatch);
273                             Slog.i(TAG, "  -- CUR: mSet="
274                                     + Arrays.toString(cur.mPref.mSetComponents));
275                             Slog.i(TAG, "  -- CUR: mComponent=" + cur.mPref.mShortComponent);
276                             Slog.i(TAG, "  -- NEW: mMatch="
277                                     + (match & IntentFilter.MATCH_CATEGORY_MASK));
278                             Slog.i(TAG, "  -- CUR: mSet=" + Arrays.toString(set));
279                             Slog.i(TAG, "  -- CUR: mComponent=" + activity.flattenToShortString());
280                         }
281                     }
282                     if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity)
283                             && cur.mPref.mMatch == (match & IntentFilter.MATCH_CATEGORY_MASK)
284                             && cur.mPref.sameSet(set)) {
285                         // Setting the preferred activity to what it happens to be already
286                         if (DEBUG_PREFERRED) {
287                             Slog.i(TAG, "Replacing with same preferred activity "
288                                     + cur.mPref.mShortComponent + " for user "
289                                     + userId + ":");
290                             filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
291                         }
292                         return;
293                     }
294                 }
295                 if (existing != null) {
296                     Settings.removeFilters(pir, filter, existing);
297                 }
298             }
299         }
300 
301         // Retake a snapshot after editing with lock held
302         addPreferredActivity(mPm.snapshotComputer(), filter, match, set, activity, true, userId,
303                 "Replacing preferred", false);
304     }
305 
306     public void clearPackagePreferredActivities(@NonNull Computer snapshot, String packageName) {
307         final int callingUid = Binder.getCallingUid();
308         if (snapshot.getInstantAppPackageName(callingUid) != null) {
309             return;
310         }
311         final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
312         if (packageState == null || !snapshot.isCallerSameApp(packageName, callingUid)) {
313             if (mPm.mContext.checkCallingOrSelfPermission(
314                     android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
315                     != PackageManager.PERMISSION_GRANTED) {
316                 if (snapshot.getUidTargetSdkVersion(callingUid)
317                         < Build.VERSION_CODES.FROYO) {
318                     Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid "
319                             + callingUid);
320                     return;
321                 }
322                 mPm.mContext.enforceCallingOrSelfPermission(
323                         android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
324             }
325         }
326         if (packageState != null && snapshot.shouldFilterApplication(packageState, callingUid,
327                 UserHandle.getUserId(callingUid))) {
328             return;
329         }
330         int callingUserId = UserHandle.getCallingUserId();
331         clearPackagePreferredActivities(packageName, callingUserId);
332     }
333 
334     /** <b>must not hold {@link #PackageManagerService.mLock}</b> */
335     void updateDefaultHomeNotLocked(@NonNull Computer snapshot, SparseBooleanArray userIds) {
336         if (Thread.holdsLock(mPm.mLock)) {
337             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
338                     + " is holding mLock", new Throwable());
339         }
340         for (int i = userIds.size() - 1; i >= 0; --i) {
341             final int userId = userIds.keyAt(i);
342             updateDefaultHomeNotLocked(snapshot, userId);
343         }
344     }
345 
346     public void setHomeActivity(@NonNull Computer snapshot, ComponentName comp, int userId) {
347         if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) {
348             return;
349         }
350         ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
351         snapshot.getHomeActivitiesAsUser(homeActivities, userId);
352 
353         boolean found = false;
354 
355         final int size = homeActivities.size();
356         final ComponentName[] set = new ComponentName[size];
357         for (int i = 0; i < size; i++) {
358             final ResolveInfo candidate = homeActivities.get(i);
359             final ActivityInfo info = candidate.activityInfo;
360             final ComponentName activityName = new ComponentName(info.packageName, info.name);
361             set[i] = activityName;
362             if (!found && activityName.equals(comp)) {
363                 found = true;
364             }
365         }
366         if (!found) {
367             throw new IllegalArgumentException("Component " + comp + " cannot be home on user "
368                     + userId);
369         }
370         replacePreferredActivity(snapshot, getHomeFilter(), IntentFilter.MATCH_CATEGORY_EMPTY,
371                 set, comp, userId);
372     }
373 
374     private WatchedIntentFilter getHomeFilter() {
375         WatchedIntentFilter filter = new WatchedIntentFilter(Intent.ACTION_MAIN);
376         filter.addCategory(Intent.CATEGORY_HOME);
377         filter.addCategory(Intent.CATEGORY_DEFAULT);
378         return filter;
379     }
380 
381     /**
382      * Variant that takes a {@link WatchedIntentFilter}
383      */
384     public void addPersistentPreferredActivity(WatchedIntentFilter filter, ComponentName activity,
385             int userId) {
386         int callingUid = Binder.getCallingUid();
387         if (callingUid != Process.SYSTEM_UID) {
388             throw new SecurityException(
389                     "addPersistentPreferredActivity can only be run by the system");
390         }
391         if (!filter.checkDataPathAndSchemeSpecificParts()) {
392             EventLog.writeEvent(0x534e4554, "246749702", callingUid);
393             throw new IllegalArgumentException("Invalid intent data paths or scheme specific parts"
394                     + " in the filter.");
395         }
396         if (filter.countActions() == 0) {
397             Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
398             return;
399         }
400         if (DEBUG_PREFERRED) {
401             Slog.i(TAG, "Adding persistent preferred activity " + activity
402                     + " for user " + userId + ":");
403             filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
404         }
405         synchronized (mPm.mLock) {
406             mPm.mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
407                     mPm.snapshotComputer(),
408                     new PersistentPreferredActivity(filter, activity, true));
409             mPm.scheduleWritePackageRestrictions(userId);
410         }
411         if (isHomeFilter(filter)) {
412             updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
413         }
414         mPm.postPreferredActivityChangedBroadcast(userId);
415     }
416 
417     public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
418         int callingUid = Binder.getCallingUid();
419         if (callingUid != Process.SYSTEM_UID) {
420             throw new SecurityException(
421                     "clearPackagePersistentPreferredActivities can only be run by the system");
422         }
423         boolean changed = false;
424         synchronized (mPm.mLock) {
425             changed = mPm.mSettings.clearPackagePersistentPreferredActivities(packageName, userId);
426         }
427         if (changed) {
428             updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
429             mPm.postPreferredActivityChangedBroadcast(userId);
430             mPm.scheduleWritePackageRestrictions(userId);
431         }
432     }
433 
434     public void clearPersistentPreferredActivity(IntentFilter filter, int userId) {
435         int callingUid = Binder.getCallingUid();
436         if (callingUid != Process.SYSTEM_UID) {
437             throw new SecurityException(
438                     "clearPersistentPreferredActivity can only be run by the system");
439         }
440         boolean changed = false;
441         synchronized (mPm.mLock) {
442             changed = mPm.mSettings.clearPersistentPreferredActivity(filter, userId);
443         }
444         if (changed) {
445             updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
446             mPm.postPreferredActivityChangedBroadcast(userId);
447             mPm.scheduleWritePackageRestrictions(userId);
448         }
449     }
450 
451     private boolean isHomeFilter(@NonNull WatchedIntentFilter filter) {
452         return filter.hasAction(Intent.ACTION_MAIN) && filter.hasCategory(Intent.CATEGORY_HOME)
453                 && filter.hasCategory(CATEGORY_DEFAULT);
454     }
455 
456     /**
457      * Common machinery for picking apart a restored XML blob and passing
458      * it to a caller-supplied functor to be applied to the running system.
459      */
460     private void restoreFromXml(TypedXmlPullParser parser, int userId,
461             String expectedStartTag, BlobXmlRestorer functor)
462             throws IOException, XmlPullParserException {
463         int type;
464         while ((type = parser.next()) != XmlPullParser.START_TAG
465                 && type != XmlPullParser.END_DOCUMENT) {
466         }
467         if (type != XmlPullParser.START_TAG) {
468             // oops didn't find a start tag?!
469             if (DEBUG_BACKUP) {
470                 Slog.e(TAG, "Didn't find start tag during restore");
471             }
472             return;
473         }
474         // this is supposed to be TAG_PREFERRED_BACKUP
475         if (!expectedStartTag.equals(parser.getName())) {
476             if (DEBUG_BACKUP) {
477                 Slog.e(TAG, "Found unexpected tag " + parser.getName());
478             }
479             return;
480         }
481 
482         // skip interfering stuff, then we're aligned with the backing implementation
483         while ((type = parser.next()) == XmlPullParser.TEXT) { }
484         functor.apply(parser, userId);
485     }
486 
487     private interface BlobXmlRestorer {
488         void apply(TypedXmlPullParser parser, int userId)
489                 throws IOException, XmlPullParserException;
490     }
491 
492     public byte[] getPreferredActivityBackup(int userId) {
493         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
494             throw new SecurityException("Only the system may call getPreferredActivityBackup()");
495         }
496 
497         ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
498         try {
499             final TypedXmlSerializer serializer = Xml.newFastSerializer();
500             serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
501             serializer.startDocument(null, true);
502             serializer.startTag(null, TAG_PREFERRED_BACKUP);
503 
504             synchronized (mPm.mLock) {
505                 mPm.mSettings.writePreferredActivitiesLPr(serializer, userId, true);
506             }
507 
508             serializer.endTag(null, TAG_PREFERRED_BACKUP);
509             serializer.endDocument();
510             serializer.flush();
511         } catch (Exception e) {
512             if (DEBUG_BACKUP) {
513                 Slog.e(TAG, "Unable to write preferred activities for backup", e);
514             }
515             return null;
516         }
517 
518         return dataStream.toByteArray();
519     }
520 
521     public void restorePreferredActivities(byte[] backup, int userId) {
522         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
523             throw new SecurityException("Only the system may call restorePreferredActivities()");
524         }
525 
526         try {
527             final TypedXmlPullParser parser = Xml.newFastPullParser();
528             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
529             restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
530                     (readParser, readUserId) -> {
531                         synchronized (mPm.mLock) {
532                             mPm.mSettings.readPreferredActivitiesLPw(readParser, readUserId);
533                         }
534                         updateDefaultHomeNotLocked(mPm.snapshotComputer(), readUserId);
535                     });
536         } catch (Exception e) {
537             if (DEBUG_BACKUP) {
538                 Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
539             }
540         }
541     }
542 
543     /**
544      * Non-Binder method, support for the backup/restore mechanism: write the
545      * default browser (etc) settings in its canonical XML format.  Returns the default
546      * browser XML representation as a byte array, or null if there is none.
547      */
548     public byte[] getDefaultAppsBackup(int userId) {
549         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
550             throw new SecurityException("Only the system may call getDefaultAppsBackup()");
551         }
552 
553         ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
554         try {
555             final TypedXmlSerializer serializer = Xml.newFastSerializer();
556             serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
557             serializer.startDocument(null, true);
558             serializer.startTag(null, TAG_DEFAULT_APPS);
559 
560             final String defaultBrowser = mPm.getDefaultBrowser(userId);
561             Settings.writeDefaultApps(serializer, defaultBrowser);
562 
563             serializer.endTag(null, TAG_DEFAULT_APPS);
564             serializer.endDocument();
565             serializer.flush();
566         } catch (Exception e) {
567             if (DEBUG_BACKUP) {
568                 Slog.e(TAG, "Unable to write default apps for backup", e);
569             }
570             return null;
571         }
572 
573         return dataStream.toByteArray();
574     }
575 
576     public void restoreDefaultApps(byte[] backup, int userId) {
577         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
578             throw new SecurityException("Only the system may call restoreDefaultApps()");
579         }
580 
581         try {
582             final TypedXmlPullParser parser = Xml.newFastPullParser();
583             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
584             restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
585                     (parser1, userId1) -> {
586                         final String defaultBrowser = Settings.readDefaultApps(parser1);
587                         if (defaultBrowser != null) {
588                             final PackageStateInternal packageState = mPm.snapshotComputer()
589                                     .getPackageStateInternal(defaultBrowser);
590                             if (packageState != null
591                                     && packageState.getUserStateOrDefault(userId1).isInstalled()) {
592                                 mPm.setDefaultBrowser(defaultBrowser, userId1);
593                             } else {
594                                 synchronized (mPm.mLock) {
595                                     mPm.mSettings.setPendingDefaultBrowserLPw(defaultBrowser,
596                                             userId1);
597                                 }
598                             }
599                         }
600                     });
601         } catch (Exception e) {
602             if (DEBUG_BACKUP) {
603                 Slog.e(TAG, "Exception restoring default apps: " + e.getMessage());
604             }
605         }
606     }
607 
608     public void resetApplicationPreferences(int userId) {
609         mPm.mContext.enforceCallingOrSelfPermission(
610                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
611         final long identity = Binder.clearCallingIdentity();
612         // writer
613         try {
614             final SparseBooleanArray changedUsers = new SparseBooleanArray();
615             synchronized (mPm.mLock) {
616                 mPm.clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
617             }
618             if (changedUsers.size() > 0) {
619                 mPm.postPreferredActivityChangedBroadcast(userId);
620             }
621             synchronized (mPm.mLock) {
622                 mPm.mSettings.applyDefaultPreferredAppsLPw(userId);
623                 mPm.mDomainVerificationManager.clearUser(userId);
624                 mPm.mPermissionManager.resetRuntimePermissionsForUser(userId);
625             }
626             updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
627             resetNetworkPolicies(userId);
628             mPm.scheduleWritePackageRestrictions(userId);
629         } finally {
630             Binder.restoreCallingIdentity(identity);
631         }
632     }
633 
634     private void resetNetworkPolicies(int userId) {
635         mPm.mInjector.getLocalService(NetworkPolicyManagerInternal.class).resetUserState(userId);
636     }
637 
638     public int getPreferredActivities(@NonNull Computer snapshot, List<IntentFilter> outFilters,
639             List<ComponentName> outActivities, String packageName) {
640         List<WatchedIntentFilter> temp =
641                 WatchedIntentFilter.toWatchedIntentFilterList(outFilters);
642         int result = getPreferredActivitiesInternal(snapshot, temp, outActivities, packageName);
643         outFilters.clear();
644         for (int i = 0; i < temp.size(); i++) {
645             outFilters.add(temp.get(i).getIntentFilter());
646         }
647         return result;
648     }
649 
650     /**
651      * Variant that takes a {@link WatchedIntentFilter}
652      */
653     private int getPreferredActivitiesInternal(@NonNull Computer snapshot,
654             List<WatchedIntentFilter> outFilters, List<ComponentName> outActivities,
655             String packageName) {
656         final int callingUid = Binder.getCallingUid();
657         if (snapshot.getInstantAppPackageName(callingUid) != null) {
658             return 0;
659         }
660         int num = 0;
661         final int userId = UserHandle.getCallingUserId();
662 
663         PreferredIntentResolver pir = snapshot.getPreferredActivities(userId);
664         if (pir != null) {
665             final Iterator<PreferredActivity> it = pir.filterIterator();
666             while (it.hasNext()) {
667                 final PreferredActivity pa = it.next();
668                 final String prefPackageName = pa.mPref.mComponent.getPackageName();
669                 if (packageName == null
670                         || (prefPackageName.equals(packageName) && pa.mPref.mAlways)) {
671                     if (snapshot.shouldFilterApplication(
672                             snapshot.getPackageStateInternal(prefPackageName), callingUid,
673                             userId)) {
674                         continue;
675                     }
676                     if (outFilters != null) {
677                         outFilters.add(new WatchedIntentFilter(pa.getIntentFilter()));
678                     }
679                     if (outActivities != null) {
680                         outActivities.add(pa.mPref.mComponent);
681                     }
682                 }
683             }
684         }
685 
686         return num;
687     }
688 
689     public ResolveInfo findPersistentPreferredActivity(@NonNull Computer snapshot, Intent intent,
690             int userId) {
691         if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
692             throw new SecurityException(
693                     "findPersistentPreferredActivity can only be run by the system");
694         }
695         if (!mPm.mUserManager.exists(userId)) {
696             return null;
697         }
698         final int callingUid = Binder.getCallingUid();
699         intent = PackageManagerServiceUtils.updateIntentForResolve(intent);
700         final String resolvedType = intent.resolveTypeIfNeeded(mPm.mContext.getContentResolver());
701         final long flags = snapshot.updateFlagsForResolve(
702                 0, userId, callingUid, false /*includeInstantApps*/,
703                 snapshot.isImplicitImageCaptureIntentAndNotSetByDpc(intent, userId, resolvedType,
704                         0));
705         final List<ResolveInfo> query = snapshot.queryIntentActivitiesInternal(intent,
706                 resolvedType, flags, userId);
707         return snapshot.findPersistentPreferredActivity(intent, resolvedType, flags, query, false,
708                 userId);
709     }
710 
711     /**
712      * Variant that takes a {@link WatchedIntentFilter}
713      */
714     public void setLastChosenActivity(@NonNull Computer snapshot, Intent intent,
715             String resolvedType, int flags, WatchedIntentFilter filter, int match,
716             ComponentName activity) {
717         if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) {
718             return;
719         }
720         final int userId = UserHandle.getCallingUserId();
721         if (DEBUG_PREFERRED) {
722             Log.v(TAG, "setLastChosenActivity intent=" + intent
723                     + " resolvedType=" + resolvedType
724                     + " flags=" + flags
725                     + " filter=" + filter
726                     + " match=" + match
727                     + " activity=" + activity);
728             filter.dump(new PrintStreamPrinter(System.out), "    ");
729         }
730         intent.setComponent(null);
731         final List<ResolveInfo> query = snapshot.queryIntentActivitiesInternal(intent,
732                 resolvedType, flags, userId);
733         // Find any earlier preferred or last chosen entries and nuke them
734         findPreferredActivityNotLocked(snapshot, intent, resolvedType, flags, query, false, true,
735                 false, userId);
736         // Add the new activity as the last chosen for this filter
737         addPreferredActivity(snapshot, filter, match, null, activity, false, userId,
738                 "Setting last chosen", false);
739     }
740 
741     public ResolveInfo getLastChosenActivity(@NonNull Computer snapshot, Intent intent,
742             String resolvedType, int flags) {
743         if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) {
744             return null;
745         }
746         final int userId = UserHandle.getCallingUserId();
747         if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
748         final List<ResolveInfo> query = snapshot.queryIntentActivitiesInternal(intent,
749                 resolvedType, flags, userId);
750         return findPreferredActivityNotLocked(snapshot, intent, resolvedType, flags, query, false,
751                 false, false, userId);
752     }
753 }
754