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