1893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz/*
2893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * Copyright 2016, The Android Open Source Project
3893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz *
4893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * Licensed under the Apache License, Version 2.0 (the "License");
5893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * you may not use this file except in compliance with the License.
6893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * You may obtain a copy of the License at
7893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz *
8893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz *     http://www.apache.org/licenses/LICENSE-2.0
9893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz *
10893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * Unless required by applicable law or agreed to in writing, software
11893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * distributed under the License is distributed on an "AS IS" BASIS,
12893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * See the License for the specific language governing permissions and
14893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * limitations under the License.
15893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz */
16893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
17893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzpackage com.android.managedprovisioning.task.nonrequiredapps;
18893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
19893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport static com.android.internal.util.Preconditions.checkNotNull;
20893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
21893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport android.annotation.Nullable;
22893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport android.app.AppGlobals;
23893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport android.content.Context;
24893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport android.content.pm.IPackageManager;
25893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport android.util.Xml;
26893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
27893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport com.android.internal.annotations.VisibleForTesting;
28893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport com.android.internal.util.FastXmlSerializer;
29893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport com.android.managedprovisioning.common.ProvisionLogger;
30893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport com.android.managedprovisioning.common.Utils;
31893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
32893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport java.io.File;
33893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport java.io.FileInputStream;
34893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport java.io.FileOutputStream;
35893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport java.io.IOException;
36893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport java.util.Collections;
37893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport java.util.HashSet;
38893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport java.util.Set;
39893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
40893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport org.xmlpull.v1.XmlPullParser;
41893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport org.xmlpull.v1.XmlPullParserException;
42893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzimport org.xmlpull.v1.XmlSerializer;
43893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
44893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz/**
45893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * Stores and retrieves the system apps that were on the device during provisioning and on
46893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz * subsequent OTAs.
47893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz */
48893e62e1faddce062d66810f078edf417c724ae4Benjamin Franzpublic class SystemAppsSnapshot {
49893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    private static final String TAG_SYSTEM_APPS = "system-apps";
50893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    private static final String TAG_PACKAGE_LIST_ITEM = "item";
51893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    private static final String ATTR_VALUE = "value";
52893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
53893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    private final Context mContext;
54893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    private final IPackageManager mIPackageManager;
55893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    private final Utils mUtils;
56893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
57893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    public SystemAppsSnapshot(Context context) {
58893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        this(context, AppGlobals.getPackageManager(), new Utils());
59893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    }
60893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
61893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    @VisibleForTesting
62893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    SystemAppsSnapshot(
63893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            Context context,
64893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            IPackageManager iPackageManager,
65893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            Utils utils) {
66893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        mContext = checkNotNull(context);
67893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        mIPackageManager = checkNotNull(iPackageManager);
68893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        mUtils = checkNotNull(utils);
69893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    }
70893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
71893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    /**
72893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     * Returns whether currently a snapshot exists for the given user.
73893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     *
74893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     * @param userId the user id for which the snapshot is requested.
75893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     */
76893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    public boolean hasSnapshot(int userId) {
77893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        return getSystemAppsFile(mContext, userId).exists();
78893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    }
79893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
80893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    /**
81893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     * Returns the last stored snapshot for the given user.
82893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     *
83893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     * @param userId the user id for which the snapshot is requested.
84893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     */
85893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    public Set<String> getSnapshot(int userId) {
86893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        return readSystemApps(getSystemAppsFile(mContext, userId));
87893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    }
88893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
89893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    /**
90893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     * Call this method to take a snapshot of the current set of system apps.
91893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     *
92893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     * @param userId the user id for which the snapshot should be taken.
93893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz     */
94893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    public void takeNewSnapshot(int userId) {
95893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        final File systemAppsFile = getSystemAppsFile(mContext, userId);
96893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        systemAppsFile.getParentFile().mkdirs(); // Creating the folder if it does not exist
97893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        writeSystemApps(mUtils.getCurrentSystemApps(mIPackageManager, userId), systemAppsFile);
98893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    }
99893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
100893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    private void writeSystemApps(Set<String> packageNames, File systemAppsFile) {
101893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        try {
102893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            FileOutputStream stream = new FileOutputStream(systemAppsFile, false);
103893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            XmlSerializer serializer = new FastXmlSerializer();
104893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            serializer.setOutput(stream, "utf-8");
105893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            serializer.startDocument(null, true);
106893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            serializer.startTag(null, TAG_SYSTEM_APPS);
107893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            for (String packageName : packageNames) {
108893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                serializer.startTag(null, TAG_PACKAGE_LIST_ITEM);
109893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                serializer.attribute(null, ATTR_VALUE, packageName);
110893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                serializer.endTag(null, TAG_PACKAGE_LIST_ITEM);
111893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            }
112893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            serializer.endTag(null, TAG_SYSTEM_APPS);
113893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            serializer.endDocument();
114893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            stream.close();
115893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        } catch (IOException e) {
116893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            ProvisionLogger.loge("IOException trying to write the system apps", e);
117893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        }
118893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    }
119893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
120893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    private Set<String> readSystemApps(File systemAppsFile) {
121893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        Set<String> result = new HashSet<>();
122893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        if (!systemAppsFile.exists()) {
123893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            return result;
124893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        }
125893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        try {
126893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            FileInputStream stream = new FileInputStream(systemAppsFile);
127893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
128893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            XmlPullParser parser = Xml.newPullParser();
129893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            parser.setInput(stream, null);
130893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            parser.next();
131893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
132893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            int type;
133893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            int outerDepth = parser.getDepth();
134893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
135893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
136893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
137893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                    continue;
138893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                }
139893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                String tag = parser.getName();
140893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                if (tag.equals(TAG_PACKAGE_LIST_ITEM)) {
141893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                    result.add(parser.getAttributeValue(null, ATTR_VALUE));
142893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                } else {
143893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                    ProvisionLogger.loge("Unknown tag: " + tag);
144893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                }
145893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            }
146893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            stream.close();
147893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        } catch (IOException e) {
148893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            ProvisionLogger.loge("IOException trying to read the system apps", e);
149893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        } catch (XmlPullParserException e) {
150893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz            ProvisionLogger.loge("XmlPullParserException trying to read the system apps", e);
151893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        }
152893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        return result;
153893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    }
154893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz
155893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    @VisibleForTesting
156893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    static File getSystemAppsFile(Context context, int userId) {
157893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz        return new File(context.getFilesDir() + File.separator + "system_apps"
158893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz                + File.separator + "user" + userId + ".xml");
159893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz    }
160893e62e1faddce062d66810f078edf417c724ae4Benjamin Franz}
161