14b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani/*
24b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * Copyright (C) 2011 The Android Open Source Project
34b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani *
44b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * Licensed under the Apache License, Version 2.0 (the "License");
54b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * you may not use this file except in compliance with the License.
64b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * You may obtain a copy of the License at
74b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani *
84b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani *      http://www.apache.org/licenses/LICENSE-2.0
94b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani *
104b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * Unless required by applicable law or agreed to in writing, software
114b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * distributed under the License is distributed on an "AS IS" BASIS,
124b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * See the License for the specific language governing permissions and
144b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani * limitations under the License.
154b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani */
164b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
174b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasanipackage com.android.server.pm;
184b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
19135936072b24b090fb63940aea41b408d855a4f3Amith Yamasaniimport com.android.internal.util.ArrayUtils;
204b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport com.android.internal.util.FastXmlSerializer;
214b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
220b285499db739ba50f2f839d633e763c70e67f96Amith Yamasaniimport android.content.pm.ApplicationInfo;
230b285499db739ba50f2f839d633e763c70e67f96Amith Yamasaniimport android.content.pm.PackageManager;
244b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport android.content.pm.UserInfo;
254b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport android.os.Environment;
264b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport android.os.FileUtils;
270b285499db739ba50f2f839d633e763c70e67f96Amith Yamasaniimport android.os.SystemClock;
28742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasaniimport android.os.UserId;
290b285499db739ba50f2f839d633e763c70e67f96Amith Yamasaniimport android.util.Log;
304b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport android.util.Slog;
314b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport android.util.SparseArray;
324b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport android.util.Xml;
334b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
344b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport java.io.BufferedOutputStream;
354b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport java.io.File;
364b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport java.io.FileInputStream;
374b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport java.io.FileOutputStream;
384b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport java.io.IOException;
394b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport java.util.ArrayList;
404b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport java.util.List;
414b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
424b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport org.xmlpull.v1.XmlPullParser;
434b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport org.xmlpull.v1.XmlPullParserException;
444b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasaniimport org.xmlpull.v1.XmlSerializer;
454b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
460b285499db739ba50f2f839d633e763c70e67f96Amith Yamasanipublic class UserManager {
474b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private static final String TAG_NAME = "name";
484b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
494b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private static final String ATTR_FLAGS = "flags";
504b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
514b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private static final String ATTR_ID = "id";
524b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
534b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private static final String TAG_USERS = "users";
544b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
554b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private static final String TAG_USER = "user";
564b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
570b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    private static final String LOG_TAG = "UserManager";
584b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
590b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    private static final String USER_INFO_DIR = "system" + File.separator + "users";
604b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private static final String USER_LIST_FILENAME = "userlist.xml";
614b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
62135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
634b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
644b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private final File mUsersDir;
654b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private final File mUserListFile;
660b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    private int[] mUserIds;
670b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
680b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    private Installer mInstaller;
690b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    private File mBaseUserPath;
704b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
714b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    /**
724b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     * Available for testing purposes.
734b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     */
740b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    UserManager(File dataDir, File baseUserPath) {
754b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        mUsersDir = new File(dataDir, USER_INFO_DIR);
764b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        mUsersDir.mkdirs();
77483f3b06ea84440a082e21b68ec2c2e54046f5a6Amith Yamasani        // Make zeroth user directory, for services to migrate their files to that location
78483f3b06ea84440a082e21b68ec2c2e54046f5a6Amith Yamasani        File userZeroDir = new File(mUsersDir, "0");
79483f3b06ea84440a082e21b68ec2c2e54046f5a6Amith Yamasani        userZeroDir.mkdirs();
800b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        mBaseUserPath = baseUserPath;
814b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        FileUtils.setPermissions(mUsersDir.toString(),
824b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                FileUtils.S_IRWXU|FileUtils.S_IRWXG
834b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
844b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                -1, -1);
854b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
864b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        readUserList();
874b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
884b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
890b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    public UserManager(Installer installer, File baseUserPath) {
900b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        this(Environment.getDataDirectory(), baseUserPath);
910b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        mInstaller = installer;
924b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
934b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
944b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    public List<UserInfo> getUsers() {
95135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        synchronized (mUsers) {
96135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
97135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            for (int i = 0; i < mUsers.size(); i++) {
98135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani                users.add(mUsers.valueAt(i));
99135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            }
100135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            return users;
101135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        }
102135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    }
103135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani
104135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    public UserInfo getUser(int userId) {
105135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        synchronized (mUsers) {
106135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            UserInfo info = mUsers.get(userId);
107135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            return info;
108135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        }
109135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    }
110135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani
111135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    public boolean exists(int userId) {
112135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        synchronized (mUsers) {
113135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            return ArrayUtils.contains(mUserIds, userId);
114135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        }
115135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    }
116135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani
117135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    public void updateUserName(int userId, String name) {
118135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        synchronized (mUsers) {
119135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            UserInfo info = mUsers.get(userId);
120135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            if (name != null && !name.equals(info.name)) {
121135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani                info.name = name;
122135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani                writeUserLocked(info);
123135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            }
1244b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
1254b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
1264b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
1270b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    /**
1280b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * Returns an array of user ids. This array is cached here for quick access, so do not modify or
1290b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * cache it elsewhere.
1300b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * @return the array of user ids.
1310b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     */
1320b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    int[] getUserIds() {
1330b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        return mUserIds;
1340b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    }
1350b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
1364b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private void readUserList() {
137135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        synchronized (mUsers) {
138135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            readUserListLocked();
139135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        }
140135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    }
141135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani
142135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    private void readUserListLocked() {
1434b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        if (!mUserListFile.exists()) {
144135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            fallbackToSingleUserLocked();
1454b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            return;
1464b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
1474b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        FileInputStream fis = null;
1484b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        try {
1494b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            fis = new FileInputStream(mUserListFile);
1504b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            XmlPullParser parser = Xml.newPullParser();
1514b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            parser.setInput(fis, null);
1524b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            int type;
1534b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            while ((type = parser.next()) != XmlPullParser.START_TAG
1544b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    && type != XmlPullParser.END_DOCUMENT) {
1554b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                ;
1564b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            }
1574b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
1584b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            if (type != XmlPullParser.START_TAG) {
1590b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani                Slog.e(LOG_TAG, "Unable to read user list");
160135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani                fallbackToSingleUserLocked();
1614b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                return;
1624b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            }
1634b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
1644b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
1654b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
1664b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    String id = parser.getAttributeValue(null, ATTR_ID);
1674b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    UserInfo user = readUser(Integer.parseInt(id));
1684b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    if (user != null) {
1694b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                        mUsers.put(user.id, user);
1704b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    }
1714b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                }
1724b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            }
173135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            updateUserIdsLocked();
1744b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        } catch (IOException ioe) {
175135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            fallbackToSingleUserLocked();
1764b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        } catch (XmlPullParserException pe) {
177135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            fallbackToSingleUserLocked();
178bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn        } finally {
179bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn            if (fis != null) {
180bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn                try {
181bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn                    fis.close();
182bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn                } catch (IOException e) {
183bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn                }
184bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn            }
1854b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
1864b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
1874b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
188135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    private void fallbackToSingleUserLocked() {
1894b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        // Create the primary user
1904b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        UserInfo primary = new UserInfo(0, "Primary",
1914b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
1924b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        mUsers.put(0, primary);
193135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        updateUserIdsLocked();
1944b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
195135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        writeUserListLocked();
196135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        writeUserLocked(primary);
1974b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
1984b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
1994b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    /*
2004b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     * Writes the user file in this format:
2014b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     *
2024b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     * <user flags="20039023" id="0">
2034b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     *   <name>Primary</name>
2044b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     * </user>
2054b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     */
206135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    private void writeUserLocked(UserInfo userInfo) {
207742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani        FileOutputStream fos = null;
2084b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        try {
2094b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
210742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani            fos = new FileOutputStream(mUserFile);
2114b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            final BufferedOutputStream bos = new BufferedOutputStream(fos);
2124b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2134b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            // XmlSerializer serializer = XmlUtils.serializerInstance();
2144b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            final XmlSerializer serializer = new FastXmlSerializer();
2154b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.setOutput(bos, "utf-8");
2164b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.startDocument(null, true);
2174b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
2184b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2194b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.startTag(null, TAG_USER);
2204b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
2214b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
2224b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2234b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.startTag(null, TAG_NAME);
2244b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.text(userInfo.name);
2254b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.endTag(null, TAG_NAME);
2264b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2274b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.endTag(null, TAG_USER);
2284b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2294b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.endDocument();
2304b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        } catch (IOException ioe) {
2310b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
232742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani        } finally {
233742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani            if (fos != null) {
234742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani                try {
235742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani                    fos.close();
236742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani                } catch (IOException ioe) {
237742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani                }
238742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani            }
2394b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
2404b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
2414b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2424b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    /*
2434b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     * Writes the user list file in this format:
2444b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     *
2454b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     * <users>
2464b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     *   <user id="0"></user>
2474b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     *   <user id="2"></user>
2484b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     * </users>
2494b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani     */
250135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    private void writeUserListLocked() {
251742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani        FileOutputStream fos = null;
2524b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        try {
253742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani            fos = new FileOutputStream(mUserListFile);
2544b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            final BufferedOutputStream bos = new BufferedOutputStream(fos);
2554b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2564b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            // XmlSerializer serializer = XmlUtils.serializerInstance();
2574b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            final XmlSerializer serializer = new FastXmlSerializer();
2584b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.setOutput(bos, "utf-8");
2594b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.startDocument(null, true);
2604b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
2614b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2624b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.startTag(null, TAG_USERS);
2634b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2644b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            for (int i = 0; i < mUsers.size(); i++) {
2654b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                UserInfo user = mUsers.valueAt(i);
2664b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                serializer.startTag(null, TAG_USER);
2674b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
2684b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                serializer.endTag(null, TAG_USER);
2694b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            }
2704b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2714b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.endTag(null, TAG_USERS);
2724b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2734b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            serializer.endDocument();
2744b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        } catch (IOException ioe) {
2750b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            Slog.e(LOG_TAG, "Error writing user list");
276742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani        } finally {
277742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani            if (fos != null) {
278742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani                try {
279742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani                    fos.close();
280742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani                } catch (IOException ioe) {
281742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani                }
282742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani            }
2834b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
2844b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
2854b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2864b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private UserInfo readUser(int id) {
2874b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        int flags = 0;
2884b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        String name = null;
2894b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
2904b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        FileInputStream fis = null;
2914b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        try {
2924b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
2934b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            fis = new FileInputStream(userFile);
2944b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            XmlPullParser parser = Xml.newPullParser();
2954b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            parser.setInput(fis, null);
2964b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            int type;
2974b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            while ((type = parser.next()) != XmlPullParser.START_TAG
2984b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    && type != XmlPullParser.END_DOCUMENT) {
2994b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                ;
3004b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            }
3014b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
3024b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            if (type != XmlPullParser.START_TAG) {
3030b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani                Slog.e(LOG_TAG, "Unable to read user " + id);
3044b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                return null;
3054b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            }
3064b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
3074b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
3084b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                String storedId = parser.getAttributeValue(null, ATTR_ID);
3094b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                if (Integer.parseInt(storedId) != id) {
3100b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani                    Slog.e(LOG_TAG, "User id does not match the file name");
3114b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    return null;
3124b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                }
3134b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
3144b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                flags = Integer.parseInt(flagString);
3154b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
3164b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                while ((type = parser.next()) != XmlPullParser.START_TAG
3174b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                        && type != XmlPullParser.END_DOCUMENT) {
3184b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                }
3194b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
3204b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    type = parser.next();
3214b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    if (type == XmlPullParser.TEXT) {
3224b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                        name = parser.getText();
3234b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                    }
3244b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                }
3254b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            }
3264b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
3274b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            UserInfo userInfo = new UserInfo(id, name, flags);
3284b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            return userInfo;
3294b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
3304b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        } catch (IOException ioe) {
3314b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        } catch (XmlPullParserException pe) {
332bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn        } finally {
333bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn            if (fis != null) {
334bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn                try {
335bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn                    fis.close();
336bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn                } catch (IOException e) {
337bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn                }
338bfd89b35bae3f679e1a219f22c07bde9eb63241bDianne Hackborn            }
3394b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
3404b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        return null;
3414b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
3424b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
343135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    public UserInfo createUser(String name, int flags) {
3440b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        int userId = getNextAvailableId();
3450b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        UserInfo userInfo = new UserInfo(userId, name, flags);
3460b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        File userPath = new File(mBaseUserPath, Integer.toString(userId));
347135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        if (!createPackageFolders(userId, userPath)) {
3484b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            return null;
3494b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
350135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        synchronized (mUsers) {
351135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            mUsers.put(userId, userInfo);
352135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            writeUserListLocked();
353135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            writeUserLocked(userInfo);
354135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            updateUserIdsLocked();
355135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        }
3564b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        return userInfo;
3574b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
3584b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
3590b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    /**
3600b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * Removes a user and all data directories created for that user. This method should be called
3610b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * after the user's processes have been terminated.
3620b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * @param id the user's id
3630b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     */
364135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    public boolean removeUser(int id) {
365135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        synchronized (mUsers) {
366135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            return removeUserLocked(id);
367135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        }
368135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    }
369135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani
370135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    private boolean removeUserLocked(int id) {
3714b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        // Remove from the list
3724b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        UserInfo userInfo = mUsers.get(id);
3734b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        if (userInfo != null) {
3744b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            // Remove this user from the list
3754b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            mUsers.remove(id);
3764b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            // Remove user file
3774b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            File userFile = new File(mUsersDir, id + ".xml");
3784b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            userFile.delete();
3790b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            // Update the user list
380135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            writeUserListLocked();
381135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            updateUserIdsLocked();
382135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani            return true;
3830b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        }
384135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        return false;
3850b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    }
3860b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
3870b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    public void installPackageForAllUsers(String packageName, int uid) {
3880b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        for (int userId : mUserIds) {
3890b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            // Don't do it for the primary user, it will become recursive.
3900b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            if (userId == 0)
3910b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani                continue;
392742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani            mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
3930b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani                    userId);
3940b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        }
3950b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    }
3960b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
3970b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    public void clearUserDataForAllUsers(String packageName) {
3980b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        for (int userId : mUserIds) {
3990b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            // Don't do it for the primary user, it will become recursive.
4000b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            if (userId == 0)
4010b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani                continue;
4020b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            mInstaller.clearUserData(packageName, userId);
4030b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        }
4040b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    }
4050b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
4060b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    public void removePackageForAllUsers(String packageName) {
4070b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        for (int userId : mUserIds) {
4080b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            // Don't do it for the primary user, it will become recursive.
4090b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            if (userId == 0)
4100b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani                continue;
4110b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            mInstaller.remove(packageName, userId);
4120b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        }
4130b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    }
4140b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
4150b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    /**
4160b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * Caches the list of user ids in an array, adjusting the array size when necessary.
4170b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     */
418135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    private void updateUserIdsLocked() {
4190b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        if (mUserIds == null || mUserIds.length != mUsers.size()) {
4200b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            mUserIds = new int[mUsers.size()];
4210b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        }
4220b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        for (int i = 0; i < mUsers.size(); i++) {
4230b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani            mUserIds[i] = mUsers.keyAt(i);
4244b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
4254b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
4264b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
4270b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani    /**
4280b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * Returns the next available user id, filling in any holes in the ids.
429742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani     * TODO: May not be a good idea to recycle ids, in case it results in confusion
430742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani     * for data and battery stats collection, or unexpected cross-talk.
4310b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     * @return
4320b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani     */
4334b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    private int getNextAvailableId() {
4344b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        int i = 0;
4354b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        while (i < Integer.MAX_VALUE) {
4364b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            if (mUsers.indexOfKey(i) < 0) {
4374b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani                break;
4384b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            }
4394b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani            i++;
4404b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        }
4414b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        return i;
4424b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
4434b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
444135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    private boolean createPackageFolders(int id, File userPath) {
4450b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        // mInstaller may not be available for unit-tests.
446135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani        if (mInstaller == null) return true;
4470b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
4480b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        // Create the user path
4490b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        userPath.mkdir();
4500b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
4510b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani                | FileUtils.S_IXOTH, -1, -1);
4520b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
453742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani        mInstaller.cloneUserData(0, id, false);
454742a67127366c376fdf188ff99ba30b27d3bf90cAmith Yamasani
4554b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        return true;
4564b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
4574b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani
458135936072b24b090fb63940aea41b408d855a4f3Amith Yamasani    boolean removePackageFolders(int id) {
4590b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        // mInstaller may not be available for unit-tests.
4600b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        if (mInstaller == null) return true;
4610b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani
4620b285499db739ba50f2f839d633e763c70e67f96Amith Yamasani        mInstaller.removeUserDataDirs(id);
4634b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani        return true;
4644b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani    }
4654b2e934928a2eb65927bd39197198c28c49efb94Amith Yamasani}
466