1 /*
2  * Copyright (C) 2020 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.internal.telephony;
18 
19 import android.annotation.Nullable;
20 import android.annotation.UserIdInt;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.PackageManager;
25 import android.os.Build;
26 import android.os.CarrierAssociatedAppEntry;
27 import android.os.SystemConfigManager;
28 import android.os.UserHandle;
29 import android.permission.LegacyPermissionManager;
30 import android.provider.Settings;
31 import android.telephony.TelephonyManager;
32 import android.util.ArrayMap;
33 import android.util.Log;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.internal.telephony.util.TelephonyUtils;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Set;
42 
43 /**
44  * Utilities for handling carrier applications.
45  * @hide
46  */
47 public final class CarrierAppUtils {
48     private static final String TAG = "CarrierAppUtils";
49 
50     private static final boolean DEBUG = false; // STOPSHIP if true
51 
CarrierAppUtils()52     private CarrierAppUtils() {}
53 
54     /**
55      * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
56      *
57      * Evaluates the list of applications in
58      * {@link SystemConfigManager#getDisabledUntilUsedPreinstalledCarrierApps()}. We want to disable
59      * each such application which is present on the system image until the user inserts a SIM
60      * which causes that application to gain carrier privilege (indicating a "match"), without
61      * interfering with the user if they opt to enable/disable the app explicitly.
62      *
63      * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
64      * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
65      * the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED.
66      *
67      * In addition, there is a list of carrier-associated applications in
68      * {@link SystemConfigManager#getDisabledUntilUsedPreinstalledCarrierAssociatedApps}. Each app
69      * in this list is associated with a carrier app. When the given carrier app is enabled/disabled
70      * per the above, the associated applications are enabled/disabled to match.
71      *
72      * When enabling a carrier app we also grant it default permissions.
73      *
74      * This method is idempotent and is safe to be called at any time; it should be called once at
75      * system startup prior to any application running, as well as any time the set of carrier
76      * privileged apps may have changed.
77      */
disableCarrierAppsUntilPrivileged(String callingPackage, TelephonyManager telephonyManager, @UserIdInt int userId, Context context)78     public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
79             TelephonyManager telephonyManager, @UserIdInt int userId, Context context) {
80         if (DEBUG) {
81             Log.d(TAG, "disableCarrierAppsUntilPrivileged");
82         }
83         SystemConfigManager config = context.getSystemService(SystemConfigManager.class);
84         Set<String> systemCarrierAppsDisabledUntilUsed =
85                 config.getDisabledUntilUsedPreinstalledCarrierApps();
86         Map<String, List<CarrierAssociatedAppEntry>> systemCarrierAssociatedAppsDisabledUntilUsed =
87                 config.getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries();
88         ContentResolver contentResolver = getContentResolverForUser(context, userId);
89         disableCarrierAppsUntilPrivileged(callingPackage, telephonyManager, contentResolver,
90                 userId, systemCarrierAppsDisabledUntilUsed,
91                 systemCarrierAssociatedAppsDisabledUntilUsed, context);
92     }
93 
94     /**
95      * Like {@link #disableCarrierAppsUntilPrivileged(String, TelephonyManager, int, Context)},
96      * but assumes that no carrier apps have carrier privileges.
97      *
98      * This prevents a potential race condition on first boot - since the app's default state is
99      * enabled, we will initially disable it when the telephony stack is first initialized as it has
100      * not yet read the carrier privilege rules. However, since telephony is initialized later on
101      * late in boot, the app being disabled may have already been started in response to certain
102      * broadcasts. The app will continue to run (briefly) after being disabled, before the Package
103      * Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
104      */
disableCarrierAppsUntilPrivileged(String callingPackage, @UserIdInt int userId, Context context)105     public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
106             @UserIdInt int userId, Context context) {
107         if (DEBUG) {
108             Log.d(TAG, "disableCarrierAppsUntilPrivileged");
109         }
110         SystemConfigManager config = context.getSystemService(SystemConfigManager.class);
111         Set<String> systemCarrierAppsDisabledUntilUsed =
112                 config.getDisabledUntilUsedPreinstalledCarrierApps();
113 
114         Map<String, List<CarrierAssociatedAppEntry>> systemCarrierAssociatedAppsDisabledUntilUsed =
115                 config.getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries();
116         ContentResolver contentResolver = getContentResolverForUser(context, userId);
117         disableCarrierAppsUntilPrivileged(callingPackage, null /* telephonyManager */,
118                 contentResolver, userId, systemCarrierAppsDisabledUntilUsed,
119                 systemCarrierAssociatedAppsDisabledUntilUsed, context);
120     }
121 
getContentResolverForUser(Context context, @UserIdInt int userId)122     private static ContentResolver getContentResolverForUser(Context context,
123             @UserIdInt int userId) {
124         Context userContext = context.createContextAsUser(UserHandle.of(userId), 0);
125         return userContext.getContentResolver();
126     }
127 
isUpdatedSystemApp(ApplicationInfo ai)128     private static boolean isUpdatedSystemApp(ApplicationInfo ai) {
129         return (ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
130     }
131 
132     /**
133      * Disable carrier apps until they are privileged
134      * Must be public b/c framework unit tests can't access package-private methods.
135      */
136     // Must be public b/c framework unit tests can't access package-private methods.
137     @VisibleForTesting
disableCarrierAppsUntilPrivileged(String callingPackage, @Nullable TelephonyManager telephonyManager, ContentResolver contentResolver, int userId, Set<String> systemCarrierAppsDisabledUntilUsed, Map<String, List<CarrierAssociatedAppEntry>> systemCarrierAssociatedAppsDisabledUntilUsed, Context context)138     public static void disableCarrierAppsUntilPrivileged(String callingPackage,
139             @Nullable TelephonyManager telephonyManager, ContentResolver contentResolver,
140             int userId, Set<String> systemCarrierAppsDisabledUntilUsed,
141             Map<String, List<CarrierAssociatedAppEntry>>
142             systemCarrierAssociatedAppsDisabledUntilUsed, Context context) {
143         PackageManager packageManager = context.getPackageManager();
144         LegacyPermissionManager permissionManager = (LegacyPermissionManager)
145                 context.getSystemService(Context.LEGACY_PERMISSION_SERVICE);
146         List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(
147                 userId, systemCarrierAppsDisabledUntilUsed, context);
148         if (candidates == null || candidates.isEmpty()) {
149             return;
150         }
151 
152         Map<String, List<AssociatedAppInfo>> associatedApps = getDefaultCarrierAssociatedAppsHelper(
153                 userId, systemCarrierAssociatedAppsDisabledUntilUsed, context);
154 
155         List<String> enabledCarrierPackages = new ArrayList<>();
156         int carrierAppsHandledSdk =
157                 Settings.Secure.getIntForUser(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED,
158                         0, contentResolver.getUserId());
159         if (DEBUG) {
160             Log.i(TAG, "Last execution SDK: " + carrierAppsHandledSdk);
161         }
162         boolean hasRunEver = carrierAppsHandledSdk != 0; // SDKs < R used to just set 1 here
163         boolean hasRunForSdk = carrierAppsHandledSdk == Build.VERSION.SDK_INT;
164 
165         try {
166             for (ApplicationInfo ai : candidates) {
167                 String packageName = ai.packageName;
168                 boolean hasPrivileges = telephonyManager != null
169                         && telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
170                                 == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
171 
172                 // add hiddenUntilInstalled flag for carrier apps and associated apps
173                 packageManager.setSystemAppState(
174                         packageName, PackageManager.SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN);
175                 List<AssociatedAppInfo> associatedAppList = associatedApps.get(packageName);
176                 if (associatedAppList != null) {
177                     for (AssociatedAppInfo associatedApp : associatedAppList) {
178                         packageManager.setSystemAppState(associatedApp.appInfo.packageName,
179                                 PackageManager.SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN);
180                     }
181                 }
182 
183                 int enabledSetting = context.createContextAsUser(UserHandle.of(userId), 0)
184                         .getPackageManager().getApplicationEnabledSetting(packageName);
185                 if (hasPrivileges) {
186                     // Only update enabled state for the app on /system. Once it has been
187                     // updated we shouldn't touch it.
188                     if (!isUpdatedSystemApp(ai) && enabledSetting
189                             == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
190                             || enabledSetting
191                             == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
192                             || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
193                         Log.i(TAG, "Update state (" + packageName + "): ENABLED for user "
194                                 + userId);
195                         context.createContextAsUser(UserHandle.of(userId), 0)
196                                 .getPackageManager()
197                                 .setSystemAppState(
198                                         packageName, PackageManager.SYSTEM_APP_STATE_INSTALLED);
199                         context.createPackageContextAsUser(callingPackage, 0, UserHandle.of(userId))
200                                 .getPackageManager()
201                                 .setApplicationEnabledSetting(
202                                         packageName,
203                                         PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
204                                         PackageManager.DONT_KILL_APP);
205                     }
206 
207                     // Also enable any associated apps for this carrier app.
208                     if (associatedAppList != null) {
209                         for (AssociatedAppInfo associatedApp : associatedAppList) {
210                             int associatedAppEnabledSetting = context
211                                     .createContextAsUser(UserHandle.of(userId), 0)
212                                     .getPackageManager()
213                                     .getApplicationEnabledSetting(
214                                             associatedApp.appInfo.packageName);
215                             boolean associatedAppInstalled = (associatedApp.appInfo.flags
216                                     & ApplicationInfo.FLAG_INSTALLED) != 0;
217                             if (DEBUG) {
218                                 Log.i(TAG, "(hasPrivileges) associated app "
219                                         + associatedApp.appInfo.packageName + ", enabled = "
220                                         + associatedAppEnabledSetting + ", installed = "
221                                         + associatedAppInstalled);
222                             }
223                             if (associatedAppEnabledSetting
224                                     == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
225                                     || associatedAppEnabledSetting
226                                     == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
227                                     || !associatedAppInstalled) {
228                                 Log.i(TAG, "Update associated state ("
229                                         + associatedApp.appInfo.packageName + "): ENABLED for user "
230                                         + userId);
231                                 context.createContextAsUser(UserHandle.of(userId), 0)
232                                         .getPackageManager()
233                                         .setSystemAppState(associatedApp.appInfo.packageName,
234                                                 PackageManager.SYSTEM_APP_STATE_INSTALLED);
235                                 context.createPackageContextAsUser(
236                                         callingPackage, 0, UserHandle.of(userId))
237                                         .getPackageManager()
238                                         .setApplicationEnabledSetting(
239                                                 associatedApp.appInfo.packageName,
240                                                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
241                                                 PackageManager.DONT_KILL_APP);
242                             }
243                         }
244                     }
245 
246                     // Always re-grant default permissions to carrier apps w/ privileges.
247                     enabledCarrierPackages.add(ai.packageName);
248                 } else {  // No carrier privileges
249                     // Only update enabled state for the app on /system. Once it has been
250                     // updated we shouldn't touch it.
251                     if (!isUpdatedSystemApp(ai) && enabledSetting
252                             == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
253                             && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
254                         Log.i(TAG, "Update state (" + packageName
255                                 + "): DISABLED_UNTIL_USED for user " + userId);
256                         context.createContextAsUser(UserHandle.of(userId), 0)
257                                 .getPackageManager()
258                                 .setSystemAppState(
259                                         packageName, PackageManager.SYSTEM_APP_STATE_UNINSTALLED);
260                     }
261 
262                     // Associated apps are more brittle, because we can't rely on the distinction
263                     // between "default" and "enabled". To account for this, we have two cases:
264                     // 1. We've never run before, so we're fine to disable all associated apps.
265                     // 2. We've run before, but not on this SDK version, so we will only operate on
266                     //    apps with addedInSdk in the range (lastHandledSdk, currentSdk].
267                     // Otherwise, don't touch the associated apps.
268                     if (associatedAppList != null) {
269                         for (AssociatedAppInfo associatedApp : associatedAppList) {
270                             boolean allowDisable = !hasRunEver || (!hasRunForSdk
271                                     && associatedApp.addedInSdk
272                                     != CarrierAssociatedAppEntry.SDK_UNSPECIFIED
273                                     && associatedApp.addedInSdk > carrierAppsHandledSdk
274                                     && associatedApp.addedInSdk <= Build.VERSION.SDK_INT);
275                             int associatedAppEnabledSetting = context
276                                     .createContextAsUser(UserHandle.of(userId), 0)
277                                     .getPackageManager()
278                                     .getApplicationEnabledSetting(
279                                             associatedApp.appInfo.packageName);
280                             boolean associatedAppInstalled = (associatedApp.appInfo.flags
281                                     & ApplicationInfo.FLAG_INSTALLED) != 0;
282                             if (DEBUG) {
283                                 Log.i(TAG, "(!hasPrivileges) associated app "
284                                         + associatedApp.appInfo.packageName + ", allowDisable = "
285                                         + allowDisable + ", addedInSdk = "
286                                         + associatedApp.addedInSdk + ", enabled = "
287                                         + associatedAppEnabledSetting + ", installed = "
288                                         + associatedAppInstalled);
289                             }
290                             if (allowDisable
291                                     && associatedAppEnabledSetting
292                                     == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
293                                     && associatedAppInstalled) {
294                                 Log.i(TAG,
295                                         "Update associated state ("
296                                         + associatedApp.appInfo.packageName
297                                         + "): DISABLED_UNTIL_USED for user " + userId);
298                                 context.createContextAsUser(UserHandle.of(userId), 0)
299                                         .getPackageManager()
300                                         .setSystemAppState(associatedApp.appInfo.packageName,
301                                                 PackageManager.SYSTEM_APP_STATE_UNINSTALLED);
302                             }
303                         }
304                     }
305                 }
306             }
307 
308             // Mark the execution so we do not disable apps again on this SDK version.
309             if (!hasRunEver || !hasRunForSdk) {
310                 Settings.Secure.putIntForUser(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED,
311                         Build.VERSION.SDK_INT, contentResolver.getUserId());
312             }
313 
314             if (!enabledCarrierPackages.isEmpty()) {
315                 // Since we enabled at least one app, ensure we grant default permissions to those
316                 // apps.
317                 String[] packageNames = new String[enabledCarrierPackages.size()];
318                 enabledCarrierPackages.toArray(packageNames);
319                 permissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames,
320                         UserHandle.of(userId), TelephonyUtils.DIRECT_EXECUTOR, isSuccess -> { });
321             }
322         } catch (PackageManager.NameNotFoundException e) {
323             Log.w(TAG, "Could not reach PackageManager", e);
324         }
325     }
326 
327     /**
328      * Returns the list of "default" carrier apps.
329      *
330      * This is the subset of apps returned by
331      * {@link #getDefaultCarrierAppCandidates(int, Context)} which currently have carrier
332      * privileges per the SIM(s) inserted in the device.
333      */
getDefaultCarrierApps( TelephonyManager telephonyManager, int userId, Context context)334     public static List<ApplicationInfo> getDefaultCarrierApps(
335             TelephonyManager telephonyManager, int userId, Context context) {
336         // Get all system apps from the default list.
337         List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(userId, context);
338         if (candidates == null || candidates.isEmpty()) {
339             return null;
340         }
341 
342         // Filter out apps without carrier privileges.
343         // Iterate from the end to avoid creating an Iterator object and because we will be removing
344         // elements from the list as we pass through it.
345         for (int i = candidates.size() - 1; i >= 0; i--) {
346             ApplicationInfo ai = candidates.get(i);
347             String packageName = ai.packageName;
348             boolean hasPrivileges =
349                     telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
350                             == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
351             if (!hasPrivileges) {
352                 candidates.remove(i);
353             }
354         }
355 
356         return candidates;
357     }
358 
359     /**
360      * Returns the list of "default" carrier app candidates.
361      *
362      * These are the apps subject to the hiding/showing logic in
363      * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, TelephonyManager, int,
364      * Context)}, as well as the apps which should have default
365      * permissions granted, when a matching SIM is inserted.
366      *
367      * Whether or not the app is actually considered a default app depends on whether the app has
368      * carrier privileges as determined by the SIMs in the device.
369      */
getDefaultCarrierAppCandidates( int userId, Context context)370     public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
371             int userId, Context context) {
372         Set<String> systemCarrierAppsDisabledUntilUsed =
373                 context.getSystemService(SystemConfigManager.class)
374                         .getDisabledUntilUsedPreinstalledCarrierApps();
375         return getDefaultCarrierAppCandidatesHelper(userId, systemCarrierAppsDisabledUntilUsed,
376                 context);
377     }
378 
getDefaultCarrierAppCandidatesHelper( int userId, Set<String> systemCarrierAppsDisabledUntilUsed, Context context)379     private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
380             int userId, Set<String> systemCarrierAppsDisabledUntilUsed, Context context) {
381         if (systemCarrierAppsDisabledUntilUsed == null
382                 || systemCarrierAppsDisabledUntilUsed.isEmpty()) {
383             return null;
384         }
385 
386         List<ApplicationInfo> apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.size());
387         for (String packageName : systemCarrierAppsDisabledUntilUsed) {
388             ApplicationInfo ai =
389                     getApplicationInfoIfSystemApp(userId, packageName, context);
390             if (ai != null) {
391                 apps.add(ai);
392             }
393         }
394         return apps;
395     }
396 
getDefaultCarrierAssociatedAppsHelper( int userId, Map<String, List<CarrierAssociatedAppEntry>> systemCarrierAssociatedAppsDisabledUntilUsed, Context context)397     private static Map<String, List<AssociatedAppInfo>> getDefaultCarrierAssociatedAppsHelper(
398             int userId, Map<String, List<CarrierAssociatedAppEntry>>
399             systemCarrierAssociatedAppsDisabledUntilUsed, Context context) {
400         int size = systemCarrierAssociatedAppsDisabledUntilUsed.size();
401         Map<String, List<AssociatedAppInfo>> associatedApps = new ArrayMap<>(size);
402         for (Map.Entry<String, List<CarrierAssociatedAppEntry>> entry
403                 : systemCarrierAssociatedAppsDisabledUntilUsed.entrySet()) {
404             String carrierAppPackage = entry.getKey();
405             List<CarrierAssociatedAppEntry> associatedAppPackages = entry.getValue();
406             for (int j = 0; j < associatedAppPackages.size(); j++) {
407                 CarrierAssociatedAppEntry associatedApp = associatedAppPackages.get(j);
408                 ApplicationInfo ai =
409                         getApplicationInfoIfSystemApp(userId, associatedApp.packageName, context);
410                 // Only update enabled state for the app on /system. Once it has been updated we
411                 // shouldn't touch it.
412                 if (ai != null && !isUpdatedSystemApp(ai)) {
413                     List<AssociatedAppInfo> appList = associatedApps.get(carrierAppPackage);
414                     if (appList == null) {
415                         appList = new ArrayList<>();
416                         associatedApps.put(carrierAppPackage, appList);
417                     }
418                     appList.add(new AssociatedAppInfo(ai, associatedApp.addedInSdk));
419                 }
420             }
421         }
422         return associatedApps;
423     }
424 
425     @Nullable
getApplicationInfoIfSystemApp( int userId, String packageName, Context context)426     private static ApplicationInfo getApplicationInfoIfSystemApp(
427             int userId, String packageName, Context context) {
428         try {
429             ApplicationInfo ai = context.createContextAsUser(UserHandle.of(userId), 0)
430                     .getPackageManager()
431                     .getApplicationInfo(packageName,
432                             PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
433                                     | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
434                                     | PackageManager.MATCH_SYSTEM_ONLY);
435             if (ai != null) {
436                 return ai;
437             }
438         } catch (PackageManager.NameNotFoundException e) {
439             Log.w(TAG, "Could not reach PackageManager", e);
440         }
441         return null;
442     }
443 
444     private static final class AssociatedAppInfo {
445         public final ApplicationInfo appInfo;
446         // Might be CarrierAssociatedAppEntry.SDK_UNSPECIFIED.
447         public final int addedInSdk;
448 
AssociatedAppInfo(ApplicationInfo appInfo, int addedInSdk)449         AssociatedAppInfo(ApplicationInfo appInfo, int addedInSdk) {
450             this.appInfo = appInfo;
451             this.addedInSdk = addedInSdk;
452         }
453     }
454 }
455