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