1/*
2 * Copyright (C) 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
17package com.android.server.devicepolicy;
18
19import android.annotation.Nullable;
20import android.app.admin.SystemUpdateInfo;
21import android.app.admin.SystemUpdatePolicy;
22import android.content.ComponentName;
23import android.content.pm.PackageManagerInternal;
24import android.content.pm.UserInfo;
25import android.os.Environment;
26import android.os.UserHandle;
27import android.os.UserManager;
28import android.os.UserManagerInternal;
29import android.util.ArrayMap;
30import android.util.AtomicFile;
31import android.util.Log;
32import android.util.Pair;
33import android.util.Slog;
34import android.util.SparseArray;
35import android.util.Xml;
36
37import com.android.internal.util.FastXmlSerializer;
38
39import org.xmlpull.v1.XmlPullParser;
40import org.xmlpull.v1.XmlPullParserException;
41import org.xmlpull.v1.XmlSerializer;
42
43import java.io.File;
44import java.io.FileOutputStream;
45import java.io.IOException;
46import java.io.InputStream;
47import java.io.PrintWriter;
48import java.nio.charset.StandardCharsets;
49import java.util.List;
50import java.util.Map;
51import java.util.Objects;
52import java.util.Set;
53
54import libcore.io.IoUtils;
55
56/**
57 * Stores and restores state for the Device and Profile owners and related device-wide information.
58 * By definition there can be only one device owner, but there may be a profile owner for each user.
59 *
60 * <p>This class is thread safe, so individual methods can safely be called without locking.
61 * However, caller must still synchronize on their side to ensure integrity between multiple calls.
62 */
63class Owners {
64    private static final String TAG = "DevicePolicyManagerService";
65
66    private static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE
67
68    private static final String DEVICE_OWNER_XML_LEGACY = "device_owner.xml";
69
70    // XML storing device owner info, system update policy and pending OTA update information.
71    private static final String DEVICE_OWNER_XML = "device_owner_2.xml";
72
73    private static final String PROFILE_OWNER_XML = "profile_owner.xml";
74
75    private static final String TAG_ROOT = "root";
76
77    private static final String TAG_DEVICE_OWNER = "device-owner";
78    private static final String TAG_DEVICE_INITIALIZER = "device-initializer";
79    private static final String TAG_SYSTEM_UPDATE_POLICY = "system-update-policy";
80    private static final String TAG_PENDING_OTA_INFO = "pending-ota-info";
81    private static final String TAG_PROFILE_OWNER = "profile-owner";
82    // Holds "context" for device-owner, this must not be show up before device-owner.
83    private static final String TAG_DEVICE_OWNER_CONTEXT = "device-owner-context";
84
85    private static final String ATTR_NAME = "name";
86    private static final String ATTR_PACKAGE = "package";
87    private static final String ATTR_COMPONENT_NAME = "component";
88    private static final String ATTR_REMOTE_BUGREPORT_URI = "remoteBugreportUri";
89    private static final String ATTR_REMOTE_BUGREPORT_HASH = "remoteBugreportHash";
90    private static final String ATTR_USERID = "userId";
91    private static final String ATTR_USER_RESTRICTIONS_MIGRATED = "userRestrictionsMigrated";
92
93    private final UserManager mUserManager;
94    private final UserManagerInternal mUserManagerInternal;
95    private final PackageManagerInternal mPackageManagerInternal;
96
97    // Internal state for the device owner package.
98    private OwnerInfo mDeviceOwner;
99
100    private int mDeviceOwnerUserId = UserHandle.USER_NULL;
101
102    // Internal state for the profile owner packages.
103    private final ArrayMap<Integer, OwnerInfo> mProfileOwners = new ArrayMap<>();
104
105    // Local system update policy controllable by device owner.
106    private SystemUpdatePolicy mSystemUpdatePolicy;
107
108    // Pending OTA info if there is one.
109    @Nullable
110    private SystemUpdateInfo mSystemUpdateInfo;
111
112    private final Object mLock = new Object();
113
114    public Owners(UserManager userManager,
115            UserManagerInternal userManagerInternal,
116            PackageManagerInternal packageManagerInternal) {
117        mUserManager = userManager;
118        mUserManagerInternal = userManagerInternal;
119        mPackageManagerInternal = packageManagerInternal;
120    }
121
122    /**
123     * Load configuration from the disk.
124     */
125    void load() {
126        synchronized (mLock) {
127            // First, try to read from the legacy file.
128            final File legacy = getLegacyConfigFileWithTestOverride();
129
130            final List<UserInfo> users = mUserManager.getUsers(true);
131
132            if (readLegacyOwnerFileLocked(legacy)) {
133                if (DEBUG) {
134                    Log.d(TAG, "Legacy config file found.");
135                }
136
137                // Legacy file exists, write to new files and remove the legacy one.
138                writeDeviceOwner();
139                for (int userId : getProfileOwnerKeys()) {
140                    writeProfileOwner(userId);
141                }
142                if (DEBUG) {
143                    Log.d(TAG, "Deleting legacy config file");
144                }
145                if (!legacy.delete()) {
146                    Slog.e(TAG, "Failed to remove the legacy setting file");
147                }
148            } else {
149                // No legacy file, read from the new format files.
150                new DeviceOwnerReadWriter().readFromFileLocked();
151
152                for (UserInfo ui : users) {
153                    new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
154                }
155            }
156            mUserManagerInternal.setDeviceManaged(hasDeviceOwner());
157            for (UserInfo ui : users) {
158                mUserManagerInternal.setUserManaged(ui.id, hasProfileOwner(ui.id));
159            }
160            if (hasDeviceOwner() && hasProfileOwner(getDeviceOwnerUserId())) {
161                Slog.w(TAG, String.format("User %d has both DO and PO, which is not supported",
162                        getDeviceOwnerUserId()));
163            }
164            pushToPackageManagerLocked();
165        }
166    }
167
168    private void pushToPackageManagerLocked() {
169        final SparseArray<String> po = new SparseArray<>();
170        for (int i = mProfileOwners.size() - 1; i >= 0; i--) {
171            po.put(mProfileOwners.keyAt(i), mProfileOwners.valueAt(i).packageName);
172        }
173        mPackageManagerInternal.setDeviceAndProfileOwnerPackages(
174                mDeviceOwnerUserId, (mDeviceOwner != null ? mDeviceOwner.packageName : null),
175                po);
176    }
177
178    String getDeviceOwnerPackageName() {
179        synchronized (mLock) {
180            return mDeviceOwner != null ? mDeviceOwner.packageName : null;
181        }
182    }
183
184    int getDeviceOwnerUserId() {
185        synchronized (mLock) {
186            return mDeviceOwnerUserId;
187        }
188    }
189
190    @Nullable
191    Pair<Integer, ComponentName> getDeviceOwnerUserIdAndComponent() {
192        synchronized (mLock) {
193            if (mDeviceOwner == null) {
194                return null;
195            } else {
196                return Pair.create(mDeviceOwnerUserId, mDeviceOwner.admin);
197            }
198        }
199    }
200
201    String getDeviceOwnerName() {
202        synchronized (mLock) {
203            return mDeviceOwner != null ? mDeviceOwner.name : null;
204        }
205    }
206
207    ComponentName getDeviceOwnerComponent() {
208        synchronized (mLock) {
209            return mDeviceOwner != null ? mDeviceOwner.admin : null;
210        }
211    }
212
213    String getDeviceOwnerRemoteBugreportUri() {
214        synchronized (mLock) {
215            return mDeviceOwner != null ? mDeviceOwner.remoteBugreportUri : null;
216        }
217    }
218
219    String getDeviceOwnerRemoteBugreportHash() {
220        synchronized (mLock) {
221            return mDeviceOwner != null ? mDeviceOwner.remoteBugreportHash : null;
222        }
223    }
224
225    void setDeviceOwner(ComponentName admin, String ownerName, int userId) {
226        if (userId < 0) {
227            Slog.e(TAG, "Invalid user id for device owner user: " + userId);
228            return;
229        }
230        synchronized (mLock) {
231            // For a newly set DO, there's no need for migration.
232            setDeviceOwnerWithRestrictionsMigrated(admin, ownerName, userId,
233                    /* userRestrictionsMigrated =*/ true);
234        }
235    }
236
237    // Note this should be only called during migration.  Normally when DO is set,
238    // userRestrictionsMigrated should always be true.
239    void setDeviceOwnerWithRestrictionsMigrated(ComponentName admin, String ownerName, int userId,
240            boolean userRestrictionsMigrated) {
241        synchronized (mLock) {
242            mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated,
243                    /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null);
244            mDeviceOwnerUserId = userId;
245
246            mUserManagerInternal.setDeviceManaged(true);
247            pushToPackageManagerLocked();
248        }
249    }
250
251    void clearDeviceOwner() {
252        synchronized (mLock) {
253            mDeviceOwner = null;
254            mDeviceOwnerUserId = UserHandle.USER_NULL;
255
256            mUserManagerInternal.setDeviceManaged(false);
257            pushToPackageManagerLocked();
258        }
259    }
260
261    void setProfileOwner(ComponentName admin, String ownerName, int userId) {
262        synchronized (mLock) {
263            // For a newly set PO, there's no need for migration.
264            mProfileOwners.put(userId, new OwnerInfo(ownerName, admin,
265                    /* userRestrictionsMigrated =*/ true, /* remoteBugreportUri =*/ null,
266                    /* remoteBugreportHash =*/ null));
267            mUserManagerInternal.setUserManaged(userId, true);
268            pushToPackageManagerLocked();
269        }
270    }
271
272    void removeProfileOwner(int userId) {
273        synchronized (mLock) {
274            mProfileOwners.remove(userId);
275            mUserManagerInternal.setUserManaged(userId, false);
276            pushToPackageManagerLocked();
277        }
278    }
279
280    ComponentName getProfileOwnerComponent(int userId) {
281        synchronized (mLock) {
282            OwnerInfo profileOwner = mProfileOwners.get(userId);
283            return profileOwner != null ? profileOwner.admin : null;
284        }
285    }
286
287    String getProfileOwnerName(int userId) {
288        synchronized (mLock) {
289            OwnerInfo profileOwner = mProfileOwners.get(userId);
290            return profileOwner != null ? profileOwner.name : null;
291        }
292    }
293
294    String getProfileOwnerPackage(int userId) {
295        synchronized (mLock) {
296            OwnerInfo profileOwner = mProfileOwners.get(userId);
297            return profileOwner != null ? profileOwner.packageName : null;
298        }
299    }
300
301    Set<Integer> getProfileOwnerKeys() {
302        synchronized (mLock) {
303            return mProfileOwners.keySet();
304        }
305    }
306
307    SystemUpdatePolicy getSystemUpdatePolicy() {
308        synchronized (mLock) {
309            return mSystemUpdatePolicy;
310        }
311    }
312
313    void setSystemUpdatePolicy(SystemUpdatePolicy systemUpdatePolicy) {
314        synchronized (mLock) {
315            mSystemUpdatePolicy = systemUpdatePolicy;
316        }
317    }
318
319    void clearSystemUpdatePolicy() {
320        synchronized (mLock) {
321            mSystemUpdatePolicy = null;
322        }
323    }
324
325    boolean hasDeviceOwner() {
326        synchronized (mLock) {
327            return mDeviceOwner != null;
328        }
329    }
330
331    boolean isDeviceOwnerUserId(int userId) {
332        synchronized (mLock) {
333            return mDeviceOwner != null && mDeviceOwnerUserId == userId;
334        }
335    }
336
337    boolean hasProfileOwner(int userId) {
338        synchronized (mLock) {
339            return getProfileOwnerComponent(userId) != null;
340        }
341    }
342
343    /**
344     * @return true if user restrictions need to be migrated for DO.
345     */
346    boolean getDeviceOwnerUserRestrictionsNeedsMigration() {
347        synchronized (mLock) {
348            return mDeviceOwner != null && !mDeviceOwner.userRestrictionsMigrated;
349        }
350    }
351
352    /**
353     * @return true if user restrictions need to be migrated for PO.
354     */
355    boolean getProfileOwnerUserRestrictionsNeedsMigration(int userId) {
356        synchronized (mLock) {
357            OwnerInfo profileOwner = mProfileOwners.get(userId);
358            return profileOwner != null && !profileOwner.userRestrictionsMigrated;
359        }
360    }
361
362    /** Sets the user restrictions migrated flag, and also writes to the file. */
363    void setDeviceOwnerUserRestrictionsMigrated() {
364        synchronized (mLock) {
365            if (mDeviceOwner != null) {
366                mDeviceOwner.userRestrictionsMigrated = true;
367            }
368            writeDeviceOwner();
369        }
370    }
371
372    /** Sets the remote bugreport uri and hash, and also writes to the file. */
373    void setDeviceOwnerRemoteBugreportUriAndHash(String remoteBugreportUri,
374            String remoteBugreportHash) {
375        synchronized (mLock) {
376            if (mDeviceOwner != null) {
377                mDeviceOwner.remoteBugreportUri = remoteBugreportUri;
378                mDeviceOwner.remoteBugreportHash = remoteBugreportHash;
379            }
380            writeDeviceOwner();
381        }
382    }
383
384    /** Sets the user restrictions migrated flag, and also writes to the file.  */
385    void setProfileOwnerUserRestrictionsMigrated(int userId) {
386        synchronized (mLock) {
387            OwnerInfo profileOwner = mProfileOwners.get(userId);
388            if (profileOwner != null) {
389                profileOwner.userRestrictionsMigrated = true;
390            }
391            writeProfileOwner(userId);
392        }
393    }
394
395    private boolean readLegacyOwnerFileLocked(File file) {
396        if (!file.exists()) {
397            // Already migrated or the device has no owners.
398            return false;
399        }
400        try {
401            InputStream input = new AtomicFile(file).openRead();
402            XmlPullParser parser = Xml.newPullParser();
403            parser.setInput(input, StandardCharsets.UTF_8.name());
404            int type;
405            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT) {
406                if (type!=XmlPullParser.START_TAG) {
407                    continue;
408                }
409
410                String tag = parser.getName();
411                if (tag.equals(TAG_DEVICE_OWNER)) {
412                    String name = parser.getAttributeValue(null, ATTR_NAME);
413                    String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
414                    mDeviceOwner = new OwnerInfo(name, packageName,
415                            /* userRestrictionsMigrated =*/ false, /* remoteBugreportUri =*/ null,
416                            /* remoteBugreportHash =*/ null);
417                    mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
418                } else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
419                    // Deprecated tag
420                } else if (tag.equals(TAG_PROFILE_OWNER)) {
421                    String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
422                    String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
423                    String profileOwnerComponentStr =
424                            parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
425                    int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID));
426                    OwnerInfo profileOwnerInfo = null;
427                    if (profileOwnerComponentStr != null) {
428                        ComponentName admin = ComponentName.unflattenFromString(
429                                profileOwnerComponentStr);
430                        if (admin != null) {
431                            profileOwnerInfo = new OwnerInfo(profileOwnerName, admin,
432                                /* userRestrictionsMigrated =*/ false, null, null);
433                        } else {
434                            // This shouldn't happen but switch from package name -> component name
435                            // might have written bad device owner files. b/17652534
436                            Slog.e(TAG, "Error parsing device-owner file. Bad component name " +
437                                    profileOwnerComponentStr);
438                        }
439                    }
440                    if (profileOwnerInfo == null) {
441                        profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName,
442                                /* userRestrictionsMigrated =*/ false,
443                                /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null);
444                    }
445                    mProfileOwners.put(userId, profileOwnerInfo);
446                } else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) {
447                    mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
448                } else {
449                    throw new XmlPullParserException(
450                            "Unexpected tag in device owner file: " + tag);
451                }
452            }
453            input.close();
454        } catch (XmlPullParserException|IOException e) {
455            Slog.e(TAG, "Error parsing device-owner file", e);
456        }
457        return true;
458    }
459
460    void writeDeviceOwner() {
461        synchronized (mLock) {
462            if (DEBUG) {
463                Log.d(TAG, "Writing to device owner file");
464            }
465            new DeviceOwnerReadWriter().writeToFileLocked();
466        }
467    }
468
469    void writeProfileOwner(int userId) {
470        synchronized (mLock) {
471            if (DEBUG) {
472                Log.d(TAG, "Writing to profile owner file for user " + userId);
473            }
474            new ProfileOwnerReadWriter(userId).writeToFileLocked();
475        }
476    }
477
478    /**
479     * Saves the given {@link SystemUpdateInfo} if it is different from the existing one, or if
480     * none exists.
481     *
482     * @return Whether the saved system update information has changed.
483     */
484    boolean saveSystemUpdateInfo(@Nullable SystemUpdateInfo newInfo) {
485        synchronized (mLock) {
486            // Check if we already have the same update information.
487            if (Objects.equals(newInfo, mSystemUpdateInfo)) {
488                return false;
489            }
490
491            mSystemUpdateInfo = newInfo;
492            new DeviceOwnerReadWriter().writeToFileLocked();
493            return true;
494        }
495    }
496
497    @Nullable
498    public SystemUpdateInfo getSystemUpdateInfo() {
499        synchronized (mLock) {
500            return mSystemUpdateInfo;
501        }
502    }
503
504    private abstract static class FileReadWriter {
505        private final File mFile;
506
507        protected FileReadWriter(File file) {
508            mFile = file;
509        }
510
511        abstract boolean shouldWrite();
512
513        void writeToFileLocked() {
514            if (!shouldWrite()) {
515                if (DEBUG) {
516                    Log.d(TAG, "No need to write to " + mFile);
517                }
518                // No contents, remove the file.
519                if (mFile.exists()) {
520                    if (DEBUG) {
521                        Log.d(TAG, "Deleting existing " + mFile);
522                    }
523                    if (!mFile.delete()) {
524                        Slog.e(TAG, "Failed to remove " + mFile.getPath());
525                    }
526                }
527                return;
528            }
529            if (DEBUG) {
530                Log.d(TAG, "Writing to " + mFile);
531            }
532
533            final AtomicFile f = new AtomicFile(mFile);
534            FileOutputStream outputStream = null;
535            try {
536                outputStream = f.startWrite();
537                final XmlSerializer out = new FastXmlSerializer();
538                out.setOutput(outputStream, StandardCharsets.UTF_8.name());
539
540                // Root tag
541                out.startDocument(null, true);
542                out.startTag(null, TAG_ROOT);
543
544                // Actual content
545                writeInner(out);
546
547                // Close root
548                out.endTag(null, TAG_ROOT);
549                out.endDocument();
550                out.flush();
551
552                // Commit the content.
553                f.finishWrite(outputStream);
554                outputStream = null;
555
556            } catch (IOException e) {
557                Slog.e(TAG, "Exception when writing", e);
558                if (outputStream != null) {
559                    f.failWrite(outputStream);
560                }
561            }
562        }
563
564        void readFromFileLocked() {
565            if (!mFile.exists()) {
566                if (DEBUG) {
567                    Log.d(TAG, "" + mFile + " doesn't exist");
568                }
569                return;
570            }
571            if (DEBUG) {
572                Log.d(TAG, "Reading from " + mFile);
573            }
574            final AtomicFile f = new AtomicFile(mFile);
575            InputStream input = null;
576            try {
577                input = f.openRead();
578                final XmlPullParser parser = Xml.newPullParser();
579                parser.setInput(input, StandardCharsets.UTF_8.name());
580
581                int type;
582                int depth = 0;
583                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
584                    switch (type) {
585                        case XmlPullParser.START_TAG:
586                            depth++;
587                            break;
588                        case XmlPullParser.END_TAG:
589                            depth--;
590                            // fallthrough
591                        default:
592                            continue;
593                    }
594                    // Check the root tag
595                    final String tag = parser.getName();
596                    if (depth == 1) {
597                        if (!TAG_ROOT.equals(tag)) {
598                            Slog.e(TAG, "Invalid root tag: " + tag);
599                            return;
600                        }
601                        continue;
602                    }
603                    // readInner() will only see START_TAG at depth >= 2.
604                    if (!readInner(parser, depth, tag)) {
605                        return; // Error
606                    }
607                }
608            } catch (XmlPullParserException | IOException e) {
609                Slog.e(TAG, "Error parsing owners information file", e);
610            } finally {
611                IoUtils.closeQuietly(input);
612            }
613        }
614
615        abstract void writeInner(XmlSerializer out) throws IOException;
616
617        abstract boolean readInner(XmlPullParser parser, int depth, String tag);
618    }
619
620    private class DeviceOwnerReadWriter extends FileReadWriter {
621
622        protected DeviceOwnerReadWriter() {
623            super(getDeviceOwnerFileWithTestOverride());
624        }
625
626        @Override
627        boolean shouldWrite() {
628            return (mDeviceOwner != null) || (mSystemUpdatePolicy != null)
629                    || (mSystemUpdateInfo != null);
630        }
631
632        @Override
633        void writeInner(XmlSerializer out) throws IOException {
634            if (mDeviceOwner != null) {
635                mDeviceOwner.writeToXml(out, TAG_DEVICE_OWNER);
636                out.startTag(null, TAG_DEVICE_OWNER_CONTEXT);
637                out.attribute(null, ATTR_USERID, String.valueOf(mDeviceOwnerUserId));
638                out.endTag(null, TAG_DEVICE_OWNER_CONTEXT);
639            }
640
641            if (mSystemUpdatePolicy != null) {
642                out.startTag(null, TAG_SYSTEM_UPDATE_POLICY);
643                mSystemUpdatePolicy.saveToXml(out);
644                out.endTag(null, TAG_SYSTEM_UPDATE_POLICY);
645            }
646
647            if (mSystemUpdateInfo != null) {
648                mSystemUpdateInfo.writeToXml(out, TAG_PENDING_OTA_INFO);
649            }
650        }
651
652        @Override
653        boolean readInner(XmlPullParser parser, int depth, String tag) {
654            if (depth > 2) {
655                return true; // Ignore
656            }
657            switch (tag) {
658                case TAG_DEVICE_OWNER:
659                    mDeviceOwner = OwnerInfo.readFromXml(parser);
660                    mDeviceOwnerUserId = UserHandle.USER_SYSTEM; // Set default
661                    break;
662                case TAG_DEVICE_OWNER_CONTEXT: {
663                    final String userIdString =
664                            parser.getAttributeValue(null, ATTR_USERID);
665                    try {
666                        mDeviceOwnerUserId = Integer.parseInt(userIdString);
667                    } catch (NumberFormatException e) {
668                        Slog.e(TAG, "Error parsing user-id " + userIdString);
669                    }
670                    break;
671                }
672                case TAG_DEVICE_INITIALIZER:
673                    // Deprecated tag
674                    break;
675                case TAG_SYSTEM_UPDATE_POLICY:
676                    mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
677                    break;
678                case TAG_PENDING_OTA_INFO:
679                    mSystemUpdateInfo = SystemUpdateInfo.readFromXml(parser);
680                    break;
681                default:
682                    Slog.e(TAG, "Unexpected tag: " + tag);
683                    return false;
684
685            }
686            return true;
687        }
688    }
689
690    private class ProfileOwnerReadWriter extends FileReadWriter {
691        private final int mUserId;
692
693        ProfileOwnerReadWriter(int userId) {
694            super(getProfileOwnerFileWithTestOverride(userId));
695            mUserId = userId;
696        }
697
698        @Override
699        boolean shouldWrite() {
700            return mProfileOwners.get(mUserId) != null;
701        }
702
703        @Override
704        void writeInner(XmlSerializer out) throws IOException {
705            final OwnerInfo profileOwner = mProfileOwners.get(mUserId);
706            if (profileOwner != null) {
707                profileOwner.writeToXml(out, TAG_PROFILE_OWNER);
708            }
709        }
710
711        @Override
712        boolean readInner(XmlPullParser parser, int depth, String tag) {
713            if (depth > 2) {
714                return true; // Ignore
715            }
716            switch (tag) {
717                case TAG_PROFILE_OWNER:
718                    mProfileOwners.put(mUserId, OwnerInfo.readFromXml(parser));
719                    break;
720                default:
721                    Slog.e(TAG, "Unexpected tag: " + tag);
722                    return false;
723
724            }
725            return true;
726        }
727    }
728
729    static class OwnerInfo {
730        public final String name;
731        public final String packageName;
732        public final ComponentName admin;
733        public boolean userRestrictionsMigrated;
734        public String remoteBugreportUri;
735        public String remoteBugreportHash;
736
737        public OwnerInfo(String name, String packageName, boolean userRestrictionsMigrated,
738                String remoteBugreportUri, String remoteBugreportHash) {
739            this.name = name;
740            this.packageName = packageName;
741            this.admin = new ComponentName(packageName, "");
742            this.userRestrictionsMigrated = userRestrictionsMigrated;
743            this.remoteBugreportUri = remoteBugreportUri;
744            this.remoteBugreportHash = remoteBugreportHash;
745        }
746
747        public OwnerInfo(String name, ComponentName admin, boolean userRestrictionsMigrated,
748                String remoteBugreportUri, String remoteBugreportHash) {
749            this.name = name;
750            this.admin = admin;
751            this.packageName = admin.getPackageName();
752            this.userRestrictionsMigrated = userRestrictionsMigrated;
753            this.remoteBugreportUri = remoteBugreportUri;
754            this.remoteBugreportHash = remoteBugreportHash;
755        }
756
757        public void writeToXml(XmlSerializer out, String tag) throws IOException {
758            out.startTag(null, tag);
759            out.attribute(null, ATTR_PACKAGE, packageName);
760            if (name != null) {
761                out.attribute(null, ATTR_NAME, name);
762            }
763            if (admin != null) {
764                out.attribute(null, ATTR_COMPONENT_NAME, admin.flattenToString());
765            }
766            out.attribute(null, ATTR_USER_RESTRICTIONS_MIGRATED,
767                    String.valueOf(userRestrictionsMigrated));
768            if (remoteBugreportUri != null) {
769                out.attribute(null, ATTR_REMOTE_BUGREPORT_URI, remoteBugreportUri);
770            }
771            if (remoteBugreportHash != null) {
772                out.attribute(null, ATTR_REMOTE_BUGREPORT_HASH, remoteBugreportHash);
773            }
774            out.endTag(null, tag);
775        }
776
777        public static OwnerInfo readFromXml(XmlPullParser parser) {
778            final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
779            final String name = parser.getAttributeValue(null, ATTR_NAME);
780            final String componentName =
781                    parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
782            final String userRestrictionsMigratedStr =
783                    parser.getAttributeValue(null, ATTR_USER_RESTRICTIONS_MIGRATED);
784            final boolean userRestrictionsMigrated =
785                    ("true".equals(userRestrictionsMigratedStr));
786            final String remoteBugreportUri = parser.getAttributeValue(null,
787                    ATTR_REMOTE_BUGREPORT_URI);
788            final String remoteBugreportHash = parser.getAttributeValue(null,
789                    ATTR_REMOTE_BUGREPORT_HASH);
790
791            // Has component name?  If so, return [name, component]
792            if (componentName != null) {
793                final ComponentName admin = ComponentName.unflattenFromString(componentName);
794                if (admin != null) {
795                    return new OwnerInfo(name, admin, userRestrictionsMigrated,
796                            remoteBugreportUri, remoteBugreportHash);
797                } else {
798                    // This shouldn't happen but switch from package name -> component name
799                    // might have written bad device owner files. b/17652534
800                    Slog.e(TAG, "Error parsing owner file. Bad component name " +
801                            componentName);
802                }
803            }
804
805            // Else, build with [name, package]
806            return new OwnerInfo(name, packageName, userRestrictionsMigrated, remoteBugreportUri,
807                    remoteBugreportHash);
808        }
809
810        public void dump(String prefix, PrintWriter pw) {
811            pw.println(prefix + "admin=" + admin);
812            pw.println(prefix + "name=" + name);
813            pw.println(prefix + "package=" + packageName);
814        }
815    }
816
817    public void dump(String prefix, PrintWriter pw) {
818        boolean needBlank = false;
819        if (mDeviceOwner != null) {
820            pw.println(prefix + "Device Owner: ");
821            mDeviceOwner.dump(prefix + "  ", pw);
822            pw.println(prefix + "  User ID: " + mDeviceOwnerUserId);
823            needBlank = true;
824        }
825        if (mSystemUpdatePolicy != null) {
826            if (needBlank) {
827                pw.println();
828            }
829            pw.println(prefix + "System Update Policy: " + mSystemUpdatePolicy);
830            needBlank = true;
831        }
832        if (mProfileOwners != null) {
833            for (Map.Entry<Integer, OwnerInfo> entry : mProfileOwners.entrySet()) {
834                if (needBlank) {
835                    pw.println();
836                }
837                pw.println(prefix + "Profile Owner (User " + entry.getKey() + "): ");
838                entry.getValue().dump(prefix + "  ", pw);
839                needBlank = true;
840            }
841        }
842        if (mSystemUpdateInfo != null) {
843            if (needBlank) {
844                pw.println();
845            }
846            pw.println(prefix + "Pending System Update: " + mSystemUpdateInfo);
847            needBlank = true;
848        }
849    }
850
851    File getLegacyConfigFileWithTestOverride() {
852        return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML_LEGACY);
853    }
854
855    File getDeviceOwnerFileWithTestOverride() {
856        return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML);
857    }
858
859    File getProfileOwnerFileWithTestOverride(int userId) {
860        return new File(Environment.getUserSystemDirectory(userId), PROFILE_OWNER_XML);
861    }
862}
863