1 /* 2 * Copyright 2020, 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.interactacrossprofiles; 18 19 20 import android.app.admin.DevicePolicyManager; 21 import android.content.Context; 22 import android.os.UserManager; 23 import android.util.Xml; 24 25 import com.android.internal.util.FastXmlSerializer; 26 import com.android.managedprovisioning.common.ProvisionLogger; 27 28 import org.xmlpull.v1.XmlPullParser; 29 import org.xmlpull.v1.XmlPullParserException; 30 import org.xmlpull.v1.XmlSerializer; 31 32 import java.io.File; 33 import java.io.FileInputStream; 34 import java.io.FileOutputStream; 35 import java.io.IOException; 36 import java.util.HashSet; 37 import java.util.Set; 38 39 /** 40 * Stores and retrieves the cross-profile apps whitelist during provisioning and on 41 * subsequent OTAs. 42 */ 43 public class CrossProfileAppsSnapshot { 44 private static final String TAG_CROSS_PROFILE_APPS = "cross-profile-apps"; 45 private static final String TAG_PACKAGE_LIST_ITEM = "item"; 46 private static final String ATTR_VALUE = "value"; 47 private static final String FOLDER_NAME = "cross_profile_apps"; 48 49 private final Context mContext; 50 CrossProfileAppsSnapshot(Context context)51 public CrossProfileAppsSnapshot(Context context) { 52 if (context == null) { 53 throw new NullPointerException(); 54 } 55 mContext = context; 56 } 57 58 /** 59 * Returns whether currently a snapshot exists for the given user. 60 * 61 * @param userId the user id for which the snapshot is requested. 62 */ hasSnapshot(int userId)63 public boolean hasSnapshot(int userId) { 64 return getCrossProfileAppsFile(mContext, userId).exists(); 65 } 66 67 /** 68 * Returns the last stored snapshot for the given user. 69 * 70 * @param userId the user id for which the snapshot is requested. 71 */ getSnapshot(int userId)72 public Set<String> getSnapshot(int userId) { 73 return readCrossProfileApps(getCrossProfileAppsFile(mContext, userId)); 74 } 75 76 /** 77 * Call this method to take a snapshot of the current cross profile apps whitelist. 78 * 79 * @param userId the user id for which the snapshot should be taken. 80 */ takeNewSnapshot(int userId)81 public void takeNewSnapshot(int userId) { 82 final File systemAppsFile = getCrossProfileAppsFile(mContext, userId); 83 systemAppsFile.getParentFile().mkdirs(); // Creating the folder if it does not exist 84 writeCrossProfileApps(getCurrentCrossProfileAppsWhitelist(), systemAppsFile); 85 } 86 getCurrentCrossProfileAppsWhitelist()87 private Set<String> getCurrentCrossProfileAppsWhitelist() { 88 DevicePolicyManager devicePolicyManager = 89 mContext.getSystemService(DevicePolicyManager.class); 90 return devicePolicyManager.getDefaultCrossProfilePackages(); 91 } 92 writeCrossProfileApps(Set<String> packageNames, File crossProfileAppsFile)93 private void writeCrossProfileApps(Set<String> packageNames, File crossProfileAppsFile) { 94 try { 95 FileOutputStream stream = new FileOutputStream(crossProfileAppsFile, false); 96 XmlSerializer serializer = new FastXmlSerializer(); 97 serializer.setOutput(stream, "utf-8"); 98 serializer.startDocument(null, true); 99 serializer.startTag(null, TAG_CROSS_PROFILE_APPS); 100 for (String packageName : packageNames) { 101 serializer.startTag(null, TAG_PACKAGE_LIST_ITEM); 102 serializer.attribute(null, ATTR_VALUE, packageName); 103 serializer.endTag(null, TAG_PACKAGE_LIST_ITEM); 104 } 105 serializer.endTag(null, TAG_CROSS_PROFILE_APPS); 106 serializer.endDocument(); 107 stream.close(); 108 } catch (IOException e) { 109 ProvisionLogger.loge("IOException trying to write the cross profile apps", e); 110 } 111 } 112 readCrossProfileApps(File systemAppsFile)113 private Set<String> readCrossProfileApps(File systemAppsFile) { 114 Set<String> result = new HashSet<>(); 115 if (!systemAppsFile.exists()) { 116 return result; 117 } 118 try { 119 FileInputStream stream = new FileInputStream(systemAppsFile); 120 121 XmlPullParser parser = Xml.newPullParser(); 122 parser.setInput(stream, null); 123 parser.next(); 124 125 int type; 126 int outerDepth = parser.getDepth(); 127 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 128 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 129 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 130 continue; 131 } 132 String tag = parser.getName(); 133 if (tag.equals(TAG_PACKAGE_LIST_ITEM)) { 134 result.add(parser.getAttributeValue(null, ATTR_VALUE)); 135 } else { 136 ProvisionLogger.loge("Unknown tag: " + tag); 137 } 138 } 139 stream.close(); 140 } catch (IOException e) { 141 ProvisionLogger.loge("IOException trying to read the cross profile apps", e); 142 } catch (XmlPullParserException e) { 143 ProvisionLogger.loge("XmlPullParserException trying to read the cross profile apps", e); 144 } 145 return result; 146 } 147 getCrossProfileAppsFile(Context context, int userId)148 private static File getCrossProfileAppsFile(Context context, int userId) { 149 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 150 int userSerialNumber = userManager.getUserSerialNumber(userId); 151 if (userSerialNumber == -1 ) { 152 throw new IllegalArgumentException("Invalid userId : " + userId); 153 } 154 return new File(getFolder(context), userSerialNumber + ".xml"); 155 } 156 getFolder(Context context)157 private static File getFolder(Context context) { 158 return new File(context.getFilesDir(), FOLDER_NAME); 159 } 160 } 161