1 /*
2  * Copyright 2016, 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.managedprovisioning.task.nonrequiredapps;
18 
19 import static com.android.internal.util.Preconditions.checkNotNull;
20 
21 import android.annotation.IntDef;
22 import android.app.AppGlobals;
23 import android.app.admin.DevicePolicyManager;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.pm.IPackageManager;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException;
30 import com.android.managedprovisioning.common.Utils;
31 import com.android.managedprovisioning.model.ProvisioningParams;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Collections;
36 import java.util.Set;
37 
38 /**
39  * Logic that calculates which system apps should be removed during profile creation and subsequent
40  * OTAs. It also decides whether a snapshot should be taken or not.
41  */
42 public class NonRequiredAppsLogic {
43 
44     @IntDef({
45             Case.OTA_LEAVE_APPS,
46             Case.OTA_REMOVE_APPS,
47             Case.NEW_PROFILE_LEAVE_APPS,
48             Case.NEW_PROFILE_REMOVE_APPS
49     })
50     @Retention(RetentionPolicy.SOURCE)
51     private @interface Case {
52         int OTA_LEAVE_APPS = 0;
53         int OTA_REMOVE_APPS = 1;
54         int NEW_PROFILE_LEAVE_APPS = 2;
55         int NEW_PROFILE_REMOVE_APPS = 3;
56     }
57 
58     private final Context mContext;
59     private final IPackageManager mIPackageManager;
60     private final DevicePolicyManager mDevicePolicyManager;
61     private final boolean mNewProfile;
62     private final ProvisioningParams mParams;
63     private final SystemAppsSnapshot mSnapshot;
64     private final Utils mUtils;
65 
NonRequiredAppsLogic( Context context, boolean newProfile, ProvisioningParams params)66     public NonRequiredAppsLogic(
67             Context context,
68             boolean newProfile,
69             ProvisioningParams params) {
70         this(
71                 context,
72                 AppGlobals.getPackageManager(),
73                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE),
74                 newProfile,
75                 params,
76                 new SystemAppsSnapshot(context),
77                 new Utils());
78     }
79 
80     @VisibleForTesting
NonRequiredAppsLogic( Context context, IPackageManager iPackageManager, DevicePolicyManager devicePolicyManager, boolean newProfile, ProvisioningParams params, SystemAppsSnapshot snapshot, Utils utils)81     NonRequiredAppsLogic(
82             Context context,
83             IPackageManager iPackageManager,
84             DevicePolicyManager devicePolicyManager,
85             boolean newProfile,
86             ProvisioningParams params,
87             SystemAppsSnapshot snapshot,
88             Utils utils) {
89         mContext = context;
90         mIPackageManager = checkNotNull(iPackageManager);
91         mDevicePolicyManager = checkNotNull(devicePolicyManager);
92         mNewProfile = newProfile;
93         mParams = checkNotNull(params);
94         mSnapshot = checkNotNull(snapshot);
95         mUtils = checkNotNull(utils);
96     }
97 
getSystemAppsToRemove(int userId)98     public Set<String> getSystemAppsToRemove(int userId) {
99         if (!shouldDeleteSystemApps(userId)) {
100             return Collections.emptySet();
101         }
102 
103         ComponentName deviceAdminComponentName;
104         try {
105             deviceAdminComponentName = mParams.inferDeviceAdminComponentName(
106                     mUtils, mContext, userId);
107         } catch (IllegalProvisioningArgumentException ex) {
108             // Should not happen
109             throw new RuntimeException("Failed to infer device admin component name", ex);
110         }
111         // Get the packages from the black/white lists
112         Set<String> packagesToDelete = mDevicePolicyManager.getDisallowedSystemApps(
113                 deviceAdminComponentName, userId, mParams.provisioningAction);
114         if (mNewProfile) {
115             return packagesToDelete;
116         }
117         filterOutSystemAppsFromOta(packagesToDelete, userId);
118         return packagesToDelete;
119     }
120 
121     /**
122      * Modifies the given set of packages by removing system apps added explicitly from the OTA.
123      */
filterOutSystemAppsFromOta(Set<String> packagesToDelete, int userId)124     private void filterOutSystemAppsFromOta(Set<String> packagesToDelete, int userId) {
125         // Start with all system apps
126         Set<String> newSystemApps = mUtils.getCurrentSystemApps(mIPackageManager, userId);
127 
128         // Remove the ones that were already present in the last snapshot
129         newSystemApps.removeAll(mSnapshot.getSnapshot(userId));
130 
131         packagesToDelete.retainAll(newSystemApps);
132     }
133 
maybeTakeSystemAppsSnapshot(int userId)134     public void maybeTakeSystemAppsSnapshot(int userId) {
135         if (shouldDeleteSystemApps(userId)) {
136             mSnapshot.takeNewSnapshot(userId);
137         }
138     }
139 
shouldDeleteSystemApps(int userId)140     private boolean shouldDeleteSystemApps(int userId) {
141         @Case int which = getCase(userId);
142         return (Case.NEW_PROFILE_REMOVE_APPS == which) || (Case.OTA_REMOVE_APPS == which);
143     }
144 
145     @Case
getCase(int userId)146     private int getCase(int userId) {
147         if (mNewProfile) {
148             if (mParams.leaveAllSystemAppsEnabled) {
149                 return Case.NEW_PROFILE_LEAVE_APPS;
150             } else {
151                 return Case.NEW_PROFILE_REMOVE_APPS;
152             }
153         } else {
154             if (mSnapshot.hasSnapshot(userId)) {
155                 return Case.OTA_REMOVE_APPS;
156             } else {
157                 return Case.OTA_LEAVE_APPS;
158             }
159         }
160     }
161 }
162