1/*
2 * Copyright 2014, 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
17package com.android.managedprovisioning.task;
18
19import static com.android.internal.util.Preconditions.checkNotNull;
20
21import android.content.Context;
22import android.content.pm.IPackageDeleteObserver;
23import android.content.pm.PackageInfo;
24import android.content.pm.PackageManager;
25
26import com.android.internal.annotations.VisibleForTesting;
27import com.android.managedprovisioning.R;
28import com.android.managedprovisioning.common.ProvisionLogger;
29import com.android.managedprovisioning.model.ProvisioningParams;
30import com.android.managedprovisioning.task.nonrequiredapps.NonRequiredAppsLogic;
31
32import java.util.HashSet;
33import java.util.Set;
34import java.util.concurrent.atomic.AtomicInteger;
35
36/**
37 * Deletes all non-required apps.
38 *
39 * This task may be run when a profile (both for managed device and managed profile) is created.
40 * In that case the firstTimeCreation flag should be true.
41 *
42 * It should also be run after a system update with firstTimeCreation false. Note that only
43 * newly installed system apps will be deleted.
44 */
45public class DeleteNonRequiredAppsTask extends AbstractProvisioningTask {
46    private final PackageManager mPm;
47    private final NonRequiredAppsLogic mLogic;
48
49    public DeleteNonRequiredAppsTask(
50            boolean firstTimeCreation,
51            Context context,
52            ProvisioningParams params,
53            Callback callback) {
54        this(
55                context,
56                params,
57                callback,
58                new NonRequiredAppsLogic(context, firstTimeCreation, params));
59    }
60
61    @VisibleForTesting
62    DeleteNonRequiredAppsTask(
63            Context context,
64            ProvisioningParams params,
65            Callback callback,
66            NonRequiredAppsLogic logic) {
67        super(context, params, callback);
68
69        mPm = checkNotNull(context.getPackageManager());
70        mLogic = checkNotNull(logic);
71    }
72
73    @Override
74    public void run(int userId) {
75        Set<String> packagesToDelete = mLogic.getSystemAppsToRemove(userId);
76        mLogic.maybeTakeSystemAppsSnapshot(userId);
77
78        // Remove all packages that are not currently installed
79        removeNonInstalledPackages(packagesToDelete, userId);
80
81        if (packagesToDelete.isEmpty()) {
82            success();
83            return;
84        }
85
86        PackageDeleteObserver packageDeleteObserver =
87                new PackageDeleteObserver(packagesToDelete.size());
88        for (String packageName : packagesToDelete) {
89            ProvisionLogger.logd("Deleting package [" + packageName + "] as user " + userId);
90            mPm.deletePackageAsUser(packageName, packageDeleteObserver,
91                    PackageManager.DELETE_SYSTEM_APP, userId);
92        }
93    }
94
95    private void removeNonInstalledPackages(Set<String> packages, int userId) {
96        Set<String> toBeRemoved = new HashSet<>();
97        for (String packageName : packages) {
98            try {
99                PackageInfo info = mPm.getPackageInfoAsUser(
100                    packageName, 0 /* default flags */,
101                    userId);
102                if (info == null) {
103                    toBeRemoved.add(packageName);
104                }
105            } catch (PackageManager.NameNotFoundException e) {
106                toBeRemoved.add(packageName);
107            }
108        }
109        packages.removeAll(toBeRemoved);
110    }
111
112    @Override
113    public int getStatusMsgId() {
114        return R.string.progress_delete_non_required_apps;
115    }
116
117    /**
118     * Runs the next task when all packages have been deleted or shuts down the activity if package
119     * deletion fails.
120     */
121    class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
122        private final AtomicInteger mPackageCount = new AtomicInteger(0);
123
124        public PackageDeleteObserver(int packageCount) {
125            this.mPackageCount.set(packageCount);
126        }
127
128        @Override
129        public void packageDeleted(String packageName, int returnCode) {
130            if (returnCode != PackageManager.DELETE_SUCCEEDED) {
131                ProvisionLogger.logw(
132                        "Could not finish the provisioning: package deletion failed");
133                error(0);
134                return;
135            }
136            int currentPackageCount = mPackageCount.decrementAndGet();
137            if (currentPackageCount == 0) {
138                ProvisionLogger.logi("All non-required system apps with launcher icon, "
139                        + "and all disallowed apps have been uninstalled.");
140                success();
141            }
142        }
143    }
144}
145