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 17 package com.android.managedprovisioning.task; 18 19 import android.content.BroadcastReceiver; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.ApplicationInfo; 24 import android.content.pm.ComponentInfo; 25 import android.content.pm.IPackageDeleteObserver; 26 import android.content.pm.IPackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ResolveInfo; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.util.Xml; 33 34 import com.android.internal.util.FastXmlSerializer; 35 import com.android.managedprovisioning.ProvisionLogger; 36 import com.android.managedprovisioning.R; 37 38 import java.io.File; 39 import java.io.FileInputStream; 40 import java.io.FileOutputStream; 41 import java.io.IOException; 42 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.HashSet; 46 import java.util.List; 47 import java.util.Set; 48 import java.util.concurrent.atomic.AtomicInteger; 49 50 import org.xmlpull.v1.XmlPullParser; 51 import org.xmlpull.v1.XmlPullParserException; 52 import org.xmlpull.v1.XmlSerializer; 53 54 /** 55 * Removes all system apps with a launcher that are not required. 56 * Also disables sharing via Bluetooth, and components that listen to 57 * 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. 60 */ 61 public class DeleteNonRequiredAppsTask { 62 private final Callback mCallback; 63 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; 70 private final boolean mNewProfile; // If we are provisioning a new managed profile/device. 71 private final boolean mDisableInstallShortcutListenersAndTelecom; 72 73 private static final String TAG_SYSTEM_APPS = "system-apps"; 74 private static final String TAG_PACKAGE_LIST_ITEM = "item"; 75 private static final String ATTR_VALUE = "value"; 76 DeleteNonRequiredAppsTask(Context context, String mdmPackageName, int userId, int requiredAppsList, int vendorRequiredAppsList, boolean newProfile, boolean disableInstallShortcutListenersAndTelecom, Callback callback)77 public DeleteNonRequiredAppsTask(Context context, String mdmPackageName, int userId, 78 int requiredAppsList, int vendorRequiredAppsList, boolean newProfile, 79 boolean disableInstallShortcutListenersAndTelecom, Callback callback) { 80 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; 88 mNewProfile = newProfile; 89 mDisableInstallShortcutListenersAndTelecom = disableInstallShortcutListenersAndTelecom; 90 } 91 run()92 public void run() { 93 if (mNewProfile) { 94 disableBluetoothSharing(); 95 } 96 deleteNonRequiredApps(); 97 } 98 99 /** 100 * Returns if this task should be run on OTA. 101 * This is indicated by the presence of the system apps file. 102 */ shouldDeleteNonRequiredApps(Context context, int userId)103 public static boolean shouldDeleteNonRequiredApps(Context context, int userId) { 104 return getSystemAppsFile(context, userId).exists(); 105 } 106 disableBluetoothSharing()107 private void disableBluetoothSharing() { 108 ProvisionLogger.logd("Disabling Bluetooth sharing."); 109 disableComponent(new ComponentName("com.android.bluetooth", 110 "com.android.bluetooth.opp.BluetoothOppLauncherActivity")); 111 } 112 deleteNonRequiredApps()113 private void deleteNonRequiredApps() { 114 ProvisionLogger.logd("Deleting non required apps."); 115 116 File systemAppsFile = getSystemAppsFile(mContext, mUserId); 117 systemAppsFile.getParentFile().mkdirs(); // Creating the folder if it does not exist 118 119 Set<String> currentApps = getCurrentSystemApps(); 120 Set<String> previousApps; 121 if (mNewProfile) { 122 // Provisioning case. 123 124 // 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 { 128 // 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(); 134 return; 135 } 136 137 previousApps = readSystemApps(systemAppsFile); 138 } 139 writeSystemApps(currentApps, systemAppsFile); 140 Set<String> newApps = currentApps; 141 newApps.removeAll(previousApps); 142 143 if (mDisableInstallShortcutListenersAndTelecom) { 144 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 } 156 } 157 } 158 Set<String> packagesToDelete = newApps; 159 packagesToDelete.removeAll(getRequiredApps()); 160 packagesToDelete.retainAll(getCurrentAppsWithLauncher()); 161 // com.android.server.telecom should not handle CALL intents in the managed profile. 162 if (mDisableInstallShortcutListenersAndTelecom && mNewProfile) { 163 packagesToDelete.add("com.android.server.telecom"); 164 } 165 if (packagesToDelete.isEmpty()) { 166 mCallback.onSuccess(); 167 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 } 179 } 180 } 181 getSystemAppsFile(Context context, int userId)182 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 */ disableReceivers(Intent intent)190 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 disableComponent(ComponentName toDisable)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); 214 } catch (Exception e) { 215 ProvisionLogger.logw("Component not found, not disabling it: " 216 + toDisable.toShortString()); 217 } 218 } 219 220 /** 221 * Returns the set of package names of apps that are in the system image, 222 * whether they have been deleted or not. 223 */ getCurrentSystemApps()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 getCurrentAppsWithLauncher()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, 246 PackageManager.GET_UNINSTALLED_PACKAGES, mUserId); 247 Set<String> apps = new HashSet<String>(); 248 for (ResolveInfo resolveInfo : resolveInfos) { 249 apps.add(resolveInfo.activityInfo.packageName); 250 } 251 return apps; 252 } 253 writeSystemApps(Set<String> packageNames, File systemAppsFile)254 private void writeSystemApps(Set<String> packageNames, File systemAppsFile) { 255 try { 256 FileOutputStream stream = new FileOutputStream(systemAppsFile, false); 257 XmlSerializer serializer = new FastXmlSerializer(); 258 serializer.setOutput(stream, "utf-8"); 259 serializer.startDocument(null, true); 260 serializer.startTag(null, TAG_SYSTEM_APPS); 261 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 } 266 serializer.endTag(null, TAG_SYSTEM_APPS); 267 serializer.endDocument(); 268 stream.close(); 269 } catch (IOException e) { 270 ProvisionLogger.loge("IOException trying to write the system apps", e); 271 } 272 } 273 readSystemApps(File systemAppsFile)274 private Set<String> readSystemApps(File systemAppsFile) { 275 Set<String> result = new HashSet<String>(); 276 if (!systemAppsFile.exists()) { 277 return result; 278 } 279 try { 280 FileInputStream stream = new FileInputStream(systemAppsFile); 281 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) { 301 ProvisionLogger.loge("IOException trying to read the system apps", e); 302 } catch (XmlPullParserException e) { 303 ProvisionLogger.loge("XmlPullParserException trying to read the system apps", e); 304 } 305 return result; 306 } 307 getRequiredApps()308 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 317 /** 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 PackageDeleteObserver(int packageCount)324 public PackageDeleteObserver(int packageCount) { 325 this.mPackageCount.set(packageCount); 326 } 327 328 @Override packageDeleted(String packageName, int returnCode)329 public void packageDeleted(String packageName, int returnCode) { 330 if (returnCode != PackageManager.DELETE_SUCCEEDED) { 331 ProvisionLogger.logw( 332 "Could not finish the provisioning: package deletion failed"); 333 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 343 public abstract static class Callback { onSuccess()344 public abstract void onSuccess(); onError()345 public abstract void onError(); 346 } 347 } 348