Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2011 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.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
     20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
     21 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
     22 
     23 import android.content.pm.ApplicationInfo;
     24 import android.content.pm.IntentFilterVerificationInfo;
     25 import android.content.pm.PackageManager;
     26 import android.content.pm.PackageParser;
     27 import android.content.pm.PackageUserState;
     28 import android.content.pm.Signature;
     29 import android.os.BaseBundle;
     30 import android.os.PersistableBundle;
     31 import android.service.pm.PackageProto;
     32 import android.util.ArraySet;
     33 import android.util.SparseArray;
     34 import android.util.proto.ProtoOutputStream;
     35 
     36 import com.android.internal.annotations.VisibleForTesting;
     37 
     38 import java.io.File;
     39 import java.util.ArrayList;
     40 import java.util.Arrays;
     41 import java.util.List;
     42 import java.util.Set;
     43 
     44 /**
     45  * Settings base class for pending and resolved classes.
     46  */
     47 public abstract class PackageSettingBase extends SettingBase {
     48 
     49     private static final int[] EMPTY_INT_ARRAY = new int[0];
     50 
     51     public final String name;
     52     final String realName;
     53 
     54     String parentPackageName;
     55     List<String> childPackageNames;
     56 
     57     /**
     58      * Path where this package was found on disk. For monolithic packages
     59      * this is path to single base APK file; for cluster packages this is
     60      * path to the cluster directory.
     61      */
     62     File codePath;
     63     String codePathString;
     64     File resourcePath;
     65     String resourcePathString;
     66 
     67     String[] usesStaticLibraries;
     68     long[] usesStaticLibrariesVersions;
     69 
     70     /**
     71      * The path under which native libraries have been unpacked. This path is
     72      * always derived at runtime, and is only stored here for cleanup when a
     73      * package is uninstalled.
     74      */
     75     @Deprecated
     76     String legacyNativeLibraryPathString;
     77 
     78     /**
     79      * The primary CPU abi for this package.
     80      */
     81     String primaryCpuAbiString;
     82 
     83     /**
     84      * The secondary CPU abi for this package.
     85      */
     86     String secondaryCpuAbiString;
     87 
     88     /**
     89      * The install time CPU override, if any. This value is written at install time
     90      * and doesn't change during the life of an install. If non-null,
     91      * {@code primaryCpuAbiString} will contain the same value.
     92      */
     93     String cpuAbiOverrideString;
     94 
     95     long timeStamp;
     96     long firstInstallTime;
     97     long lastUpdateTime;
     98     long versionCode;
     99 
    100     boolean uidError;
    101 
    102     PackageSignatures signatures;
    103 
    104     boolean installPermissionsFixed;
    105 
    106     PackageKeySetData keySetData = new PackageKeySetData();
    107 
    108     static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
    109 
    110     // Whether this package is currently stopped, thus can not be
    111     // started until explicitly launched by the user.
    112     private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
    113 
    114     /**
    115      * Non-persisted value. During an "upgrade without restart", we need the set
    116      * of all previous code paths so we can surgically add the new APKs to the
    117      * active classloader. If at any point an application is upgraded with a
    118      * restart, this field will be cleared since the classloader would be created
    119      * using the full set of code paths when the package's process is started.
    120      */
    121     Set<String> oldCodePaths;
    122 
    123     /** Package name of the app that installed this package */
    124     String installerPackageName;
    125     /** Indicates if the package that installed this app has been uninstalled */
    126     boolean isOrphaned;
    127     /** UUID of {@link VolumeInfo} hosting this app */
    128     String volumeUuid;
    129     /** The category of this app, as hinted by the installer */
    130     int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
    131     /** Whether or not an update is available. Ostensibly only for instant apps. */
    132     boolean updateAvailable;
    133 
    134     IntentFilterVerificationInfo verificationInfo;
    135 
    136     PackageSettingBase(String name, String realName, File codePath, File resourcePath,
    137             String legacyNativeLibraryPathString, String primaryCpuAbiString,
    138             String secondaryCpuAbiString, String cpuAbiOverrideString,
    139             long pVersionCode, int pkgFlags, int pkgPrivateFlags,
    140             String parentPackageName, List<String> childPackageNames,
    141             String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
    142         super(pkgFlags, pkgPrivateFlags);
    143         this.name = name;
    144         this.realName = realName;
    145         this.parentPackageName = parentPackageName;
    146         this.childPackageNames = (childPackageNames != null)
    147                 ? new ArrayList<>(childPackageNames) : null;
    148         this.usesStaticLibraries = usesStaticLibraries;
    149         this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
    150         init(codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString,
    151                 secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode);
    152     }
    153 
    154     /**
    155      * New instance of PackageSetting with one-level-deep cloning.
    156      * <p>
    157      * IMPORTANT: With a shallow copy, we do NOT create new contained objects.
    158      * This means, for example, changes to the user state of the original PackageSetting
    159      * will also change the user state in its copy.
    160      */
    161     PackageSettingBase(PackageSettingBase base, String realName) {
    162         super(base);
    163         name = base.name;
    164         this.realName = realName;
    165         doCopy(base);
    166     }
    167 
    168     void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
    169               String primaryCpuAbiString, String secondaryCpuAbiString,
    170               String cpuAbiOverrideString, long pVersionCode) {
    171         this.codePath = codePath;
    172         this.codePathString = codePath.toString();
    173         this.resourcePath = resourcePath;
    174         this.resourcePathString = resourcePath.toString();
    175         this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
    176         this.primaryCpuAbiString = primaryCpuAbiString;
    177         this.secondaryCpuAbiString = secondaryCpuAbiString;
    178         this.cpuAbiOverrideString = cpuAbiOverrideString;
    179         this.versionCode = pVersionCode;
    180         this.signatures = new PackageSignatures();
    181     }
    182 
    183     public void setInstallerPackageName(String packageName) {
    184         installerPackageName = packageName;
    185     }
    186 
    187     public String getInstallerPackageName() {
    188         return installerPackageName;
    189     }
    190 
    191     public void setVolumeUuid(String volumeUuid) {
    192         this.volumeUuid = volumeUuid;
    193     }
    194 
    195     public String getVolumeUuid() {
    196         return volumeUuid;
    197     }
    198 
    199     public void setTimeStamp(long newStamp) {
    200         timeStamp = newStamp;
    201     }
    202 
    203     public void setUpdateAvailable(boolean updateAvailable) {
    204         this.updateAvailable = updateAvailable;
    205     }
    206 
    207     public boolean isUpdateAvailable() {
    208         return updateAvailable;
    209     }
    210 
    211     public boolean isSharedUser() {
    212         return false;
    213     }
    214 
    215     public Signature[] getSignatures() {
    216         return signatures.mSigningDetails.signatures;
    217     }
    218 
    219     public PackageParser.SigningDetails getSigningDetails() {
    220         return signatures.mSigningDetails;
    221     }
    222 
    223     /**
    224      * Makes a shallow copy of the given package settings.
    225      *
    226      * NOTE: For some fields [such as keySetData, signatures, userState, verificationInfo, etc...],
    227      * the original object is copied and a new one is not created.
    228      */
    229     public void copyFrom(PackageSettingBase orig) {
    230         super.copyFrom(orig);
    231         doCopy(orig);
    232     }
    233 
    234     private void doCopy(PackageSettingBase orig) {
    235         childPackageNames = (orig.childPackageNames != null)
    236                 ? new ArrayList<>(orig.childPackageNames) : null;
    237         codePath = orig.codePath;
    238         codePathString = orig.codePathString;
    239         cpuAbiOverrideString = orig.cpuAbiOverrideString;
    240         firstInstallTime = orig.firstInstallTime;
    241         installPermissionsFixed = orig.installPermissionsFixed;
    242         installerPackageName = orig.installerPackageName;
    243         isOrphaned = orig.isOrphaned;
    244         keySetData = orig.keySetData;
    245         lastUpdateTime = orig.lastUpdateTime;
    246         legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
    247         // Intentionally skip oldCodePaths; it's not relevant for copies
    248         parentPackageName = orig.parentPackageName;
    249         primaryCpuAbiString = orig.primaryCpuAbiString;
    250         resourcePath = orig.resourcePath;
    251         resourcePathString = orig.resourcePathString;
    252         secondaryCpuAbiString = orig.secondaryCpuAbiString;
    253         signatures = orig.signatures;
    254         timeStamp = orig.timeStamp;
    255         uidError = orig.uidError;
    256         userState.clear();
    257         for (int i=0; i<orig.userState.size(); i++) {
    258             userState.put(orig.userState.keyAt(i), orig.userState.valueAt(i));
    259         }
    260         verificationInfo = orig.verificationInfo;
    261         versionCode = orig.versionCode;
    262         volumeUuid = orig.volumeUuid;
    263         categoryHint = orig.categoryHint;
    264         usesStaticLibraries = orig.usesStaticLibraries != null
    265                 ? Arrays.copyOf(orig.usesStaticLibraries,
    266                         orig.usesStaticLibraries.length) : null;
    267         usesStaticLibrariesVersions = orig.usesStaticLibrariesVersions != null
    268                 ? Arrays.copyOf(orig.usesStaticLibrariesVersions,
    269                        orig.usesStaticLibrariesVersions.length) : null;
    270         updateAvailable = orig.updateAvailable;
    271     }
    272 
    273     private PackageUserState modifyUserState(int userId) {
    274         PackageUserState state = userState.get(userId);
    275         if (state == null) {
    276             state = new PackageUserState();
    277             userState.put(userId, state);
    278         }
    279         return state;
    280     }
    281 
    282     public PackageUserState readUserState(int userId) {
    283         PackageUserState state = userState.get(userId);
    284         if (state == null) {
    285             return DEFAULT_USER_STATE;
    286         }
    287         state.categoryHint = categoryHint;
    288         return state;
    289     }
    290 
    291     void setEnabled(int state, int userId, String callingPackage) {
    292         PackageUserState st = modifyUserState(userId);
    293         st.enabled = state;
    294         st.lastDisableAppCaller = callingPackage;
    295     }
    296 
    297     int getEnabled(int userId) {
    298         return readUserState(userId).enabled;
    299     }
    300 
    301     String getLastDisabledAppCaller(int userId) {
    302         return readUserState(userId).lastDisableAppCaller;
    303     }
    304 
    305     void setInstalled(boolean inst, int userId) {
    306         modifyUserState(userId).installed = inst;
    307     }
    308 
    309     boolean getInstalled(int userId) {
    310         return readUserState(userId).installed;
    311     }
    312 
    313     int getInstallReason(int userId) {
    314         return readUserState(userId).installReason;
    315     }
    316 
    317     void setInstallReason(int installReason, int userId) {
    318         modifyUserState(userId).installReason = installReason;
    319     }
    320 
    321     void setOverlayPaths(List<String> overlayPaths, int userId) {
    322         modifyUserState(userId).overlayPaths = overlayPaths == null ? null :
    323             overlayPaths.toArray(new String[overlayPaths.size()]);
    324     }
    325 
    326     String[] getOverlayPaths(int userId) {
    327         return readUserState(userId).overlayPaths;
    328     }
    329 
    330     /** Only use for testing. Do NOT use in production code. */
    331     @VisibleForTesting
    332     SparseArray<PackageUserState> getUserState() {
    333         return userState;
    334     }
    335 
    336     boolean isAnyInstalled(int[] users) {
    337         for (int user: users) {
    338             if (readUserState(user).installed) {
    339                 return true;
    340             }
    341         }
    342         return false;
    343     }
    344 
    345     int[] queryInstalledUsers(int[] users, boolean installed) {
    346         int num = 0;
    347         for (int user : users) {
    348             if (getInstalled(user) == installed) {
    349                 num++;
    350             }
    351         }
    352         int[] res = new int[num];
    353         num = 0;
    354         for (int user : users) {
    355             if (getInstalled(user) == installed) {
    356                 res[num] = user;
    357                 num++;
    358             }
    359         }
    360         return res;
    361     }
    362 
    363     long getCeDataInode(int userId) {
    364         return readUserState(userId).ceDataInode;
    365     }
    366 
    367     void setCeDataInode(long ceDataInode, int userId) {
    368         modifyUserState(userId).ceDataInode = ceDataInode;
    369     }
    370 
    371     boolean getStopped(int userId) {
    372         return readUserState(userId).stopped;
    373     }
    374 
    375     void setStopped(boolean stop, int userId) {
    376         modifyUserState(userId).stopped = stop;
    377     }
    378 
    379     boolean getNotLaunched(int userId) {
    380         return readUserState(userId).notLaunched;
    381     }
    382 
    383     void setNotLaunched(boolean stop, int userId) {
    384         modifyUserState(userId).notLaunched = stop;
    385     }
    386 
    387     boolean getHidden(int userId) {
    388         return readUserState(userId).hidden;
    389     }
    390 
    391     void setHidden(boolean hidden, int userId) {
    392         modifyUserState(userId).hidden = hidden;
    393     }
    394 
    395     boolean getSuspended(int userId) {
    396         return readUserState(userId).suspended;
    397     }
    398 
    399     void setSuspended(boolean suspended, String suspendingPackage, String dialogMessage,
    400             PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
    401         final PackageUserState existingUserState = modifyUserState(userId);
    402         existingUserState.suspended = suspended;
    403         existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
    404         existingUserState.dialogMessage = suspended ? dialogMessage : null;
    405         existingUserState.suspendedAppExtras = suspended ? appExtras : null;
    406         existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
    407     }
    408 
    409     public boolean getInstantApp(int userId) {
    410         return readUserState(userId).instantApp;
    411     }
    412 
    413     void setInstantApp(boolean instantApp, int userId) {
    414         modifyUserState(userId).instantApp = instantApp;
    415     }
    416 
    417     boolean getVirtulalPreload(int userId) {
    418         return readUserState(userId).virtualPreload;
    419     }
    420 
    421     void setVirtualPreload(boolean virtualPreload, int userId) {
    422         modifyUserState(userId).virtualPreload = virtualPreload;
    423     }
    424 
    425     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
    426             boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
    427             String dialogMessage, PersistableBundle suspendedAppExtras,
    428             PersistableBundle suspendedLauncherExtras, boolean instantApp,
    429             boolean virtualPreload, String lastDisableAppCaller,
    430             ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
    431             int domainVerifState, int linkGeneration, int installReason,
    432             String harmfulAppWarning) {
    433         PackageUserState state = modifyUserState(userId);
    434         state.ceDataInode = ceDataInode;
    435         state.enabled = enabled;
    436         state.installed = installed;
    437         state.stopped = stopped;
    438         state.notLaunched = notLaunched;
    439         state.hidden = hidden;
    440         state.suspended = suspended;
    441         state.suspendingPackage = suspendingPackage;
    442         state.dialogMessage = dialogMessage;
    443         state.suspendedAppExtras = suspendedAppExtras;
    444         state.suspendedLauncherExtras = suspendedLauncherExtras;
    445         state.lastDisableAppCaller = lastDisableAppCaller;
    446         state.enabledComponents = enabledComponents;
    447         state.disabledComponents = disabledComponents;
    448         state.domainVerificationStatus = domainVerifState;
    449         state.appLinkGeneration = linkGeneration;
    450         state.installReason = installReason;
    451         state.instantApp = instantApp;
    452         state.virtualPreload = virtualPreload;
    453         state.harmfulAppWarning = harmfulAppWarning;
    454     }
    455 
    456     ArraySet<String> getEnabledComponents(int userId) {
    457         return readUserState(userId).enabledComponents;
    458     }
    459 
    460     ArraySet<String> getDisabledComponents(int userId) {
    461         return readUserState(userId).disabledComponents;
    462     }
    463 
    464     void setEnabledComponents(ArraySet<String> components, int userId) {
    465         modifyUserState(userId).enabledComponents = components;
    466     }
    467 
    468     void setDisabledComponents(ArraySet<String> components, int userId) {
    469         modifyUserState(userId).disabledComponents = components;
    470     }
    471 
    472     void setEnabledComponentsCopy(ArraySet<String> components, int userId) {
    473         modifyUserState(userId).enabledComponents = components != null
    474                 ? new ArraySet<String>(components) : null;
    475     }
    476 
    477     void setDisabledComponentsCopy(ArraySet<String> components, int userId) {
    478         modifyUserState(userId).disabledComponents = components != null
    479                 ? new ArraySet<String>(components) : null;
    480     }
    481 
    482     PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
    483         PackageUserState state = modifyUserState(userId);
    484         if (disabled && state.disabledComponents == null) {
    485             state.disabledComponents = new ArraySet<String>(1);
    486         }
    487         if (enabled && state.enabledComponents == null) {
    488             state.enabledComponents = new ArraySet<String>(1);
    489         }
    490         return state;
    491     }
    492 
    493     void addDisabledComponent(String componentClassName, int userId) {
    494         modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
    495     }
    496 
    497     void addEnabledComponent(String componentClassName, int userId) {
    498         modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
    499     }
    500 
    501     boolean enableComponentLPw(String componentClassName, int userId) {
    502         PackageUserState state = modifyUserStateComponents(userId, false, true);
    503         boolean changed = state.disabledComponents != null
    504                 ? state.disabledComponents.remove(componentClassName) : false;
    505         changed |= state.enabledComponents.add(componentClassName);
    506         return changed;
    507     }
    508 
    509     boolean disableComponentLPw(String componentClassName, int userId) {
    510         PackageUserState state = modifyUserStateComponents(userId, true, false);
    511         boolean changed = state.enabledComponents != null
    512                 ? state.enabledComponents.remove(componentClassName) : false;
    513         changed |= state.disabledComponents.add(componentClassName);
    514         return changed;
    515     }
    516 
    517     boolean restoreComponentLPw(String componentClassName, int userId) {
    518         PackageUserState state = modifyUserStateComponents(userId, true, true);
    519         boolean changed = state.disabledComponents != null
    520                 ? state.disabledComponents.remove(componentClassName) : false;
    521         changed |= state.enabledComponents != null
    522                 ? state.enabledComponents.remove(componentClassName) : false;
    523         return changed;
    524     }
    525 
    526     int getCurrentEnabledStateLPr(String componentName, int userId) {
    527         PackageUserState state = readUserState(userId);
    528         if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) {
    529             return COMPONENT_ENABLED_STATE_ENABLED;
    530         } else if (state.disabledComponents != null
    531                 && state.disabledComponents.contains(componentName)) {
    532             return COMPONENT_ENABLED_STATE_DISABLED;
    533         } else {
    534             return COMPONENT_ENABLED_STATE_DEFAULT;
    535         }
    536     }
    537 
    538     void removeUser(int userId) {
    539         userState.delete(userId);
    540     }
    541 
    542     public int[] getNotInstalledUserIds() {
    543         int count = 0;
    544         int userStateCount = userState.size();
    545         for (int i = 0; i < userStateCount; i++) {
    546             if (userState.valueAt(i).installed == false) {
    547                 count++;
    548             }
    549         }
    550         if (count == 0) return EMPTY_INT_ARRAY;
    551         int[] excludedUserIds = new int[count];
    552         int idx = 0;
    553         for (int i = 0; i < userStateCount; i++) {
    554             if (userState.valueAt(i).installed == false) {
    555                 excludedUserIds[idx++] = userState.keyAt(i);
    556             }
    557         }
    558         return excludedUserIds;
    559     }
    560 
    561     IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
    562         return verificationInfo;
    563     }
    564 
    565     void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
    566         verificationInfo = info;
    567     }
    568 
    569     // Returns a packed value as a long:
    570     //
    571     // high 'int'-sized word: link status: undefined/ask/never/always.
    572     // low 'int'-sized word: relative priority among 'always' results.
    573     long getDomainVerificationStatusForUser(int userId) {
    574         PackageUserState state = readUserState(userId);
    575         long result = (long) state.appLinkGeneration;
    576         result |= ((long) state.domainVerificationStatus) << 32;
    577         return result;
    578     }
    579 
    580     void setDomainVerificationStatusForUser(final int status, int generation, int userId) {
    581         PackageUserState state = modifyUserState(userId);
    582         state.domainVerificationStatus = status;
    583         if (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
    584             state.appLinkGeneration = generation;
    585         }
    586     }
    587 
    588     void clearDomainVerificationStatusForUser(int userId) {
    589         modifyUserState(userId).domainVerificationStatus =
    590                 PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
    591     }
    592 
    593     protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
    594         int count = userState.size();
    595         for (int i = 0; i < count; i++) {
    596             final long userToken = proto.start(fieldId);
    597             final int userId = userState.keyAt(i);
    598             final PackageUserState state = userState.valueAt(i);
    599             proto.write(PackageProto.UserInfoProto.ID, userId);
    600             final int installType;
    601             if (state.instantApp) {
    602                 installType = PackageProto.UserInfoProto.INSTANT_APP_INSTALL;
    603             } else if (state.installed) {
    604                 installType = PackageProto.UserInfoProto.FULL_APP_INSTALL;
    605             } else {
    606                 installType = PackageProto.UserInfoProto.NOT_INSTALLED_FOR_USER;
    607             }
    608             proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
    609             proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
    610             proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
    611             if (state.suspended) {
    612                 proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage);
    613             }
    614             proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped);
    615             proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched);
    616             proto.write(PackageProto.UserInfoProto.ENABLED_STATE, state.enabled);
    617             proto.write(
    618                     PackageProto.UserInfoProto.LAST_DISABLED_APP_CALLER,
    619                     state.lastDisableAppCaller);
    620             proto.end(userToken);
    621         }
    622     }
    623 
    624     void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
    625         PackageUserState userState = modifyUserState(userId);
    626         userState.harmfulAppWarning = harmfulAppWarning;
    627     }
    628 
    629     String getHarmfulAppWarning(int userId) {
    630         PackageUserState userState = readUserState(userId);
    631         return userState.harmfulAppWarning;
    632     }
    633 }
    634