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