blob: 924ff25f9b6d368c89bae9f2969303c273649b69 [file] [log] [blame]
Nicolas Prevot5f22bf52014-07-24 19:30:55 +01001/*
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
Sander Alewijnse2818d322014-05-20 14:54:13 +010017package com.android.managedprovisioning.task;
18
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010019import android.content.BroadcastReceiver;
Nicolas Prevotc319f8d2014-08-19 12:02:13 +010020import android.content.ComponentName;
Sander Alewijnse2818d322014-05-20 14:54:13 +010021import android.content.Context;
22import android.content.Intent;
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010023import android.content.pm.ApplicationInfo;
Nicolas Prevotb20503c2014-08-26 17:05:33 +010024import android.content.pm.ComponentInfo;
Adam Connors012c9bb2014-07-14 08:59:16 +000025import android.content.pm.IPackageDeleteObserver;
Sander Alewijnse2818d322014-05-20 14:54:13 +010026import android.content.pm.IPackageManager;
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010027import android.content.pm.PackageManager.NameNotFoundException;
Sander Alewijnse2818d322014-05-20 14:54:13 +010028import android.content.pm.PackageManager;
29import android.content.pm.ResolveInfo;
30import android.os.RemoteException;
31import android.os.ServiceManager;
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010032import android.util.Xml;
Sander Alewijnse2818d322014-05-20 14:54:13 +010033
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010034import com.android.internal.util.FastXmlSerializer;
Sander Alewijnse2818d322014-05-20 14:54:13 +010035import com.android.managedprovisioning.ProvisionLogger;
36import com.android.managedprovisioning.R;
37
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010038import java.io.File;
39import java.io.FileInputStream;
40import java.io.FileOutputStream;
41import java.io.IOException;
42
Nicolas Prevotb20503c2014-08-26 17:05:33 +010043import java.util.ArrayList;
Sander Alewijnse2818d322014-05-20 14:54:13 +010044import java.util.Arrays;
45import java.util.HashSet;
46import java.util.List;
47import java.util.Set;
Adam Connors012c9bb2014-07-14 08:59:16 +000048import java.util.concurrent.atomic.AtomicInteger;
Sander Alewijnse2818d322014-05-20 14:54:13 +010049
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010050import org.xmlpull.v1.XmlPullParser;
51import org.xmlpull.v1.XmlPullParserException;
52import org.xmlpull.v1.XmlSerializer;
53
Sander Alewijnse2818d322014-05-20 14:54:13 +010054/**
Nicolas Prevot418aaf42014-09-08 17:51:54 +010055 * Removes all system apps with a launcher that are not required.
Benjamin Franz17f24072014-10-15 17:51:38 +010056 * Also disables sharing via Bluetooth, and components that listen to
Nicolas Prevot418aaf42014-09-08 17:51:54 +010057 * ACTION_INSTALL_SHORTCUT.
58 * This class is called a first time when a user is created, but also after a system update.
59 * In this case, it checks if the system apps that have been added need to be disabled.
Sander Alewijnse2818d322014-05-20 14:54:13 +010060 */
61public class DeleteNonRequiredAppsTask {
Sander Alewijnse28bffd62014-06-05 10:54:26 +010062 private final Callback mCallback;
Sander Alewijnse2818d322014-05-20 14:54:13 +010063 private final Context mContext;
64 private final IPackageManager mIpm;
65 private final String mMdmPackageName;
66 private final PackageManager mPm;
67 private final int mReqAppsList;
68 private final int mVendorReqAppsList;
69 private final int mUserId;
Nicolas Prevot7d56b902014-11-03 15:26:06 +000070 private final boolean mNewProfile; // If we are provisioning a new managed profile/device.
Sander Alewijnsec629b422014-11-21 11:29:53 +000071 private final boolean mDisableInstallShortcutListenersAndTelecom;
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010072
Nicolas Prevot418aaf42014-09-08 17:51:54 +010073 private static final String TAG_SYSTEM_APPS = "system-apps";
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010074 private static final String TAG_PACKAGE_LIST_ITEM = "item";
75 private static final String ATTR_VALUE = "value";
Sander Alewijnse2818d322014-05-20 14:54:13 +010076
77 public DeleteNonRequiredAppsTask(Context context, String mdmPackageName, int userId,
Nicolas Prevot418aaf42014-09-08 17:51:54 +010078 int requiredAppsList, int vendorRequiredAppsList, boolean newProfile,
Sander Alewijnsec629b422014-11-21 11:29:53 +000079 boolean disableInstallShortcutListenersAndTelecom, Callback callback) {
Sander Alewijnse2818d322014-05-20 14:54:13 +010080 mCallback = callback;
81 mContext = context;
82 mMdmPackageName = mdmPackageName;
83 mUserId = userId;
84 mIpm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
85 mPm = context.getPackageManager();
86 mReqAppsList = requiredAppsList;
87 mVendorReqAppsList = vendorRequiredAppsList;
Nicolas Prevot418aaf42014-09-08 17:51:54 +010088 mNewProfile = newProfile;
Sander Alewijnsec629b422014-11-21 11:29:53 +000089 mDisableInstallShortcutListenersAndTelecom = disableInstallShortcutListenersAndTelecom;
Sander Alewijnse2818d322014-05-20 14:54:13 +010090 }
91
92 public void run() {
Nicolas Prevot418aaf42014-09-08 17:51:54 +010093 if (mNewProfile) {
Benjamin Franz17f24072014-10-15 17:51:38 +010094 disableBluetoothSharing();
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +010095 }
96 deleteNonRequiredApps();
97 }
Nicolas Prevotb20503c2014-08-26 17:05:33 +010098
Nicolas Prevot7d56b902014-11-03 15:26:06 +000099 /**
100 * Returns if this task should be run on OTA.
101 * This is indicated by the presence of the system apps file.
102 */
103 public static boolean shouldDeleteNonRequiredApps(Context context, int userId) {
104 return getSystemAppsFile(context, userId).exists();
105 }
106
Benjamin Franz17f24072014-10-15 17:51:38 +0100107 private void disableBluetoothSharing() {
108 ProvisionLogger.logd("Disabling Bluetooth sharing.");
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100109 disableComponent(new ComponentName("com.android.bluetooth",
Nicolas Prevotb20503c2014-08-26 17:05:33 +0100110 "com.android.bluetooth.opp.BluetoothOppLauncherActivity"));
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100111 }
Nicolas Prevotb20503c2014-08-26 17:05:33 +0100112
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100113 private void deleteNonRequiredApps() {
Nicolas Prevotc319f8d2014-08-19 12:02:13 +0100114 ProvisionLogger.logd("Deleting non required apps.");
Sander Alewijnse2818d322014-05-20 14:54:13 +0100115
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000116 File systemAppsFile = getSystemAppsFile(mContext, mUserId);
117 systemAppsFile.getParentFile().mkdirs(); // Creating the folder if it does not exist
Sander Alewijnse2818d322014-05-20 14:54:13 +0100118
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100119 Set<String> currentApps = getCurrentSystemApps();
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100120 Set<String> previousApps;
121 if (mNewProfile) {
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000122 // Provisioning case.
123
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100124 // If this userId was a managed profile before, file may exist. In this case, we ignore
125 // what is in this file.
126 previousApps = new HashSet<String>();
127 } else {
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000128 // OTA case.
129
130 if (!systemAppsFile.exists()) {
131 // Error, this task should not have been run.
132 ProvisionLogger.loge("No system apps list found for user " + mUserId);
133 mCallback.onError();
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100134 return;
135 }
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000136
137 previousApps = readSystemApps(systemAppsFile);
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100138 }
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000139 writeSystemApps(currentApps, systemAppsFile);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100140 Set<String> newApps = currentApps;
141 newApps.removeAll(previousApps);
142
Sander Alewijnsec629b422014-11-21 11:29:53 +0000143 if (mDisableInstallShortcutListenersAndTelecom) {
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100144 Intent actionShortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
145 if (previousApps.isEmpty()) {
146 // Here, all the apps are in newApps.
147 // It is faster to do it this way than to go through all the apps one by one.
148 disableReceivers(actionShortcut);
149 } else {
150 // Here, all the apps are not in newApps. So we have to go through all the new
151 // apps one by one.
152 for (String newApp : newApps) {
153 actionShortcut.setPackage(newApp);
154 disableReceivers(actionShortcut);
155 }
Sander Alewijnse2818d322014-05-20 14:54:13 +0100156 }
157 }
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100158 Set<String> packagesToDelete = newApps;
159 packagesToDelete.removeAll(getRequiredApps());
160 packagesToDelete.retainAll(getCurrentAppsWithLauncher());
Tyler Gunn1c897ab2014-09-12 11:11:32 -0700161 // com.android.server.telecom should not handle CALL intents in the managed profile.
Sander Alewijnsec629b422014-11-21 11:29:53 +0000162 if (mDisableInstallShortcutListenersAndTelecom && mNewProfile) {
Tyler Gunn1c897ab2014-09-12 11:11:32 -0700163 packagesToDelete.add("com.android.server.telecom");
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100164 }
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000165 if (packagesToDelete.isEmpty()) {
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100166 mCallback.onSuccess();
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000167 return;
168 }
169 PackageDeleteObserver packageDeleteObserver =
170 new PackageDeleteObserver(packagesToDelete.size());
171 for (String packageName : packagesToDelete) {
172 try {
173 mIpm.deletePackageAsUser(packageName, packageDeleteObserver, mUserId,
174 PackageManager.DELETE_SYSTEM_APP);
175 } catch (RemoteException neverThrown) {
176 // Never thrown, as we are making local calls.
177 ProvisionLogger.loge("This should not happen.", neverThrown);
178 }
Sander Alewijnse2818d322014-05-20 14:54:13 +0100179 }
180 }
181
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000182 static File getSystemAppsFile(Context context, int userId) {
183 return new File(context.getFilesDir() + File.separator + "system_apps"
184 + File.separator + "user" + userId + ".xml");
185 }
186
187 /**
188 * Disable all components that can handle the specified broadcast intent.
189 */
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100190 private void disableReceivers(Intent intent) {
191 List<ResolveInfo> receivers = mPm.queryBroadcastReceivers(intent, 0, mUserId);
192 for (ResolveInfo ri : receivers) {
193 // One of ri.activityInfo, ri.serviceInfo, ri.providerInfo is not null. Let's find which
194 // one.
195 ComponentInfo ci;
196 if (ri.activityInfo != null) {
197 ci = ri.activityInfo;
198 } else if (ri.serviceInfo != null) {
199 ci = ri.serviceInfo;
200 } else {
201 ci = ri.providerInfo;
202 }
203 disableComponent(new ComponentName(ci.packageName, ci.name));
204 }
205 }
206
207 private void disableComponent(ComponentName toDisable) {
208 try {
209 mIpm.setComponentEnabledSetting(toDisable,
210 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP,
211 mUserId);
212 } catch (RemoteException neverThrown) {
213 ProvisionLogger.loge("This should not happen.", neverThrown);
Jessica Hummel1bfee7d2014-09-25 10:08:40 +0100214 } catch (Exception e) {
215 ProvisionLogger.logw("Component not found, not disabling it: "
216 + toDisable.toShortString());
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100217 }
218 }
219
220 /**
221 * Returns the set of package names of apps that are in the system image,
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100222 * whether they have been deleted or not.
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100223 */
224 private Set<String> getCurrentSystemApps() {
225 Set<String> apps = new HashSet<String>();
226 List<ApplicationInfo> aInfos = null;
227 try {
228 aInfos = mIpm.getInstalledApplications(
229 PackageManager.GET_UNINSTALLED_PACKAGES, mUserId).getList();
230 } catch (RemoteException neverThrown) {
231 // Never thrown, as we are making local calls.
232 ProvisionLogger.loge("This should not happen.", neverThrown);
233 }
234 for (ApplicationInfo aInfo : aInfos) {
235 if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
236 apps.add(aInfo.packageName);
237 }
238 }
239 return apps;
240 }
241
242 private Set<String> getCurrentAppsWithLauncher() {
243 Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
244 launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
245 List<ResolveInfo> resolveInfos = mPm.queryIntentActivitiesAsUser(launcherIntent,
Alexandra Gherghina5689fcd2014-09-09 12:03:18 +0100246 PackageManager.GET_UNINSTALLED_PACKAGES, mUserId);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100247 Set<String> apps = new HashSet<String>();
248 for (ResolveInfo resolveInfo : resolveInfos) {
249 apps.add(resolveInfo.activityInfo.packageName);
250 }
251 return apps;
252 }
253
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000254 private void writeSystemApps(Set<String> packageNames, File systemAppsFile) {
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100255 try {
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000256 FileOutputStream stream = new FileOutputStream(systemAppsFile, false);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100257 XmlSerializer serializer = new FastXmlSerializer();
258 serializer.setOutput(stream, "utf-8");
259 serializer.startDocument(null, true);
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100260 serializer.startTag(null, TAG_SYSTEM_APPS);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100261 for (String packageName : packageNames) {
262 serializer.startTag(null, TAG_PACKAGE_LIST_ITEM);
263 serializer.attribute(null, ATTR_VALUE, packageName);
264 serializer.endTag(null, TAG_PACKAGE_LIST_ITEM);
265 }
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100266 serializer.endTag(null, TAG_SYSTEM_APPS);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100267 serializer.endDocument();
268 stream.close();
269 } catch (IOException e) {
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100270 ProvisionLogger.loge("IOException trying to write the system apps", e);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100271 }
272 }
273
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000274 private Set<String> readSystemApps(File systemAppsFile) {
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100275 Set<String> result = new HashSet<String>();
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000276 if (!systemAppsFile.exists()) {
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100277 return result;
278 }
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100279 try {
Nicolas Prevot7d56b902014-11-03 15:26:06 +0000280 FileInputStream stream = new FileInputStream(systemAppsFile);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100281
282 XmlPullParser parser = Xml.newPullParser();
283 parser.setInput(stream, null);
284
285 int type = parser.next();
286 int outerDepth = parser.getDepth();
287 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
288 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
289 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
290 continue;
291 }
292 String tag = parser.getName();
293 if (tag.equals(TAG_PACKAGE_LIST_ITEM)) {
294 result.add(parser.getAttributeValue(null, ATTR_VALUE));
295 } else {
296 ProvisionLogger.loge("Unknown tag: " + tag);
297 }
298 }
299 stream.close();
300 } catch (IOException e) {
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100301 ProvisionLogger.loge("IOException trying to read the system apps", e);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100302 } catch (XmlPullParserException e) {
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100303 ProvisionLogger.loge("XmlPullParserException trying to read the system apps", e);
Nicolas Prevot3b76f0d2014-09-03 15:33:42 +0100304 }
305 return result;
306 }
307
Sander Alewijnse2818d322014-05-20 14:54:13 +0100308 protected Set<String> getRequiredApps() {
309 HashSet<String> requiredApps = new HashSet<String> (Arrays.asList(
310 mContext.getResources().getStringArray(mReqAppsList)));
311 requiredApps.addAll(Arrays.asList(
312 mContext.getResources().getStringArray(mVendorReqAppsList)));
313 requiredApps.add(mMdmPackageName);
314 return requiredApps;
315 }
316
Adam Connors012c9bb2014-07-14 08:59:16 +0000317 /**
318 * Runs the next task when all packages have been deleted or shuts down the activity if package
319 * deletion fails.
320 */
321 class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
322 private final AtomicInteger mPackageCount = new AtomicInteger(0);
323
324 public PackageDeleteObserver(int packageCount) {
325 this.mPackageCount.set(packageCount);
326 }
327
328 @Override
329 public void packageDeleted(String packageName, int returnCode) {
330 if (returnCode != PackageManager.DELETE_SUCCEEDED) {
331 ProvisionLogger.logw(
Nicolas Prevot418aaf42014-09-08 17:51:54 +0100332 "Could not finish the provisioning: package deletion failed");
Adam Connors012c9bb2014-07-14 08:59:16 +0000333 mCallback.onError();
334 }
335 int currentPackageCount = mPackageCount.decrementAndGet();
336 if (currentPackageCount == 0) {
337 ProvisionLogger.logi("All non-required system apps have been uninstalled.");
338 mCallback.onSuccess();
339 }
340 }
341 }
342
Sander Alewijnse2818d322014-05-20 14:54:13 +0100343 public abstract static class Callback {
344 public abstract void onSuccess();
345 public abstract void onError();
346 }
347}