Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 776c555d authored by Adam Connors's avatar Adam Connors
Browse files

Extend DeviceOwner concept to accommodate ProfileOwners

ProfileOwners, like DeviceOwners, are Device Admins that have
additional priviledges. ProfileOwners however are scoped per
user.

Change-Id: I1e22c85878e0672121e6ebbe97fca38591f992b2
parent cb1b23b5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -4771,6 +4771,7 @@ package android.app.admin {
    method public boolean isActivePasswordSufficient();
    method public boolean isAdminActive(android.content.ComponentName);
    method public boolean isDeviceOwnerApp(java.lang.String);
    method public boolean isProfileOwnerApp(java.lang.String);
    method public void lockNow();
    method public void removeActiveAdmin(android.content.ComponentName);
    method public boolean resetPassword(java.lang.String, int);
+85 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -1681,4 +1682,88 @@ public class DevicePolicyManager {
        }
        return null;
    }

    /**
     * @hide
     * Sets the given package as the profile owner of the given user profile. The package must
     * already be installed and there shouldn't be an existing profile owner registered for this
     * user. Also, this method must be called before the user has been used for the first time.
     * @param packageName the package name of the application to be registered as profile owner.
     * @param ownerName the human readable name of the organisation associated with this DPM.
     * @return whether the package was successfully registered as the profile owner.
     * @throws IllegalArgumentException if packageName is null, the package isn't installed, or
     *         the user has already been set up.
     */
    public boolean setProfileOwner(String packageName, String ownerName)
            throws IllegalArgumentException {
        if (mService != null) {
            try {
                return mService.setProfileOwner(packageName, ownerName,
                        Process.myUserHandle().getIdentifier());
            } catch (RemoteException re) {
                Log.w(TAG, "Failed to set profile owner", re);
                throw new IllegalArgumentException("Couldn't set profile owner.", re);
            }
        }
        return false;
    }

    /**
     * Used to determine if a particular package is registered as the Profile Owner for the
     * current user. A profile owner is a special device admin that has additional priviledges
     * within the managed profile.
     *
     * @param packageName The package name of the app to compare with the registered profile owner.
     * @return Whether or not the package is registered as the profile owner.
     */
    public boolean isProfileOwnerApp(String packageName) {
        if (mService != null) {
            try {
                String profileOwnerPackage = mService.getProfileOwner(
                        Process.myUserHandle().getIdentifier());
                return profileOwnerPackage != null && profileOwnerPackage.equals(packageName);
            } catch (RemoteException re) {
                Log.w(TAG, "Failed to check profile owner");
            }
        }
        return false;
    }

    /**
     * @hide
     * @return the packageName of the owner of the given user profile or null if no profile
     * owner has been set for that user.
     * @throws IllegalArgumentException if the userId is invalid.
     */
    public String getProfileOwner() throws IllegalArgumentException {
        if (mService != null) {
            try {
                return mService.getProfileOwner(Process.myUserHandle().getIdentifier());
            } catch (RemoteException re) {
                Log.w(TAG, "Failed to get profile owner");
                throw new IllegalArgumentException(
                        "Requested profile owner for invalid userId", re);
            }
        }
        return null;
    }

    /**
     * @hide
     * @return the human readable name of the organisation associated with this DPM or null if
     *         one is not set.
     * @throws IllegalArgumentException if the userId is invalid.
     */
    public String getProfileOwnerName() throws IllegalArgumentException {
        if (mService != null) {
            try {
                return mService.getProfileOwnerName(Process.myUserHandle().getIdentifier());
            } catch (RemoteException re) {
                Log.w(TAG, "Failed to get profile owner");
                throw new IllegalArgumentException(
                        "Requested profile owner for invalid userId", re);
            }
        }
        return null;
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -103,6 +103,10 @@ interface IDevicePolicyManager {
    String getDeviceOwner();
    String getDeviceOwnerName();

    boolean setProfileOwner(String packageName, String ownerName, int userHandle);
    String getProfileOwner(int userHandle);
    String getProfileOwnerName(int userHandle);

    boolean installCaCert(in byte[] certBuffer);
    void uninstallCaCert(in byte[] certBuffer);
}
+274 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.devicepolicy;

import android.app.AppGlobals;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Environment;
import android.os.RemoteException;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;

/**
 * Stores and restores state for the Device and Profile owners. By definition there can be
 * only one device owner, but there may be a profile owner for each user.
 */
public class DeviceOwner {
    private static final String TAG = "DevicePolicyManagerService";

    private static final String DEVICE_OWNER_XML = "device_owner.xml";
    private static final String TAG_DEVICE_OWNER = "device-owner";
    private static final String TAG_PROFILE_OWNER = "profile-owner";
    private static final String ATTR_NAME = "name";
    private static final String ATTR_PACKAGE = "package";
    private static final String ATTR_USERID = "userId";

    private AtomicFile fileForWriting;

    // Input/Output streams for testing.
    private InputStream mInputStreamForTest;
    private OutputStream mOutputStreamForTest;

    // Internal state for the device owner package.
    private String mDeviceOwnerPackageName;
    private String mDeviceOwnerName;

    // Internal state for the profile owner packages.
    private final HashMap<Integer, String[]> mProfileOwners = new HashMap<Integer, String[]>();

    // Private default constructor.
    private DeviceOwner() {
    }

    @VisibleForTesting
    DeviceOwner(InputStream in, OutputStream out) {
        mInputStreamForTest = in;
        mOutputStreamForTest = out;
    }

    /**
     * Loads the device owner state from disk.
     */
    static DeviceOwner load() {
        DeviceOwner owner = new DeviceOwner();
        if (new File(Environment.getSystemSecureDirectory(), DEVICE_OWNER_XML).exists()) {
            owner.readOwnerFile();
            return owner;
        } else {
            return null;
        }
    }

    /**
     * Creates an instance of the device owner object with the device owner set.
     */
    static DeviceOwner createWithDeviceOwner(String packageName, String ownerName) {
        DeviceOwner owner = new DeviceOwner();
        owner.mDeviceOwnerPackageName = packageName;
        owner.mDeviceOwnerName = ownerName;
        return owner;
    }

    /**
     * Creates an instance of the device owner object with the profile owner set.
     */
    static DeviceOwner createWithProfileOwner(String packageName, String ownerName, int userId) {
        DeviceOwner owner = new DeviceOwner();
        owner.mProfileOwners.put(userId, new String[] { packageName, ownerName });
        return owner;
    }

    String getDeviceOwnerPackageName() {
        return mDeviceOwnerPackageName;
    }

    String getDeviceOwnerName() {
        return mDeviceOwnerName;
    }

    void setDeviceOwner(String packageName, String ownerName) {
        mDeviceOwnerPackageName = packageName;
        mDeviceOwnerName = ownerName;
    }

    void setProfileOwner(String packageName, String ownerName, int userId) {
        mProfileOwners.put(userId, new String[] { packageName, ownerName });
    }

    void removeProfileOwner(int userId) {
        mProfileOwners.remove(userId);
    }

    String getProfileOwnerPackageName(int userId) {
        String[] profileOwner = mProfileOwners.get(userId);
        return profileOwner != null ? profileOwner[0] : null;
    }

    String getProfileOwnerName(int userId) {
        String[] profileOwner = mProfileOwners.get(userId);
        return profileOwner != null ? profileOwner[1] : null;
    }

    boolean hasDeviceOwner() {
        return mDeviceOwnerPackageName != null;
    }

    static boolean isInstalled(String packageName, PackageManager pm) {
        try {
            PackageInfo pi;
            if ((pi = pm.getPackageInfo(packageName, 0)) != null) {
                if ((pi.applicationInfo.flags) != 0) {
                    return true;
                }
            }
        } catch (NameNotFoundException nnfe) {
            Slog.w(TAG, "Device Owner package " + packageName + " not installed.");
        }
        return false;
    }

    static boolean isInstalledForUser(String packageName, int userHandle) {
        try {
            PackageInfo pi = (AppGlobals.getPackageManager())
                    .getPackageInfo(packageName, 0, userHandle);
            if (pi != null && pi.applicationInfo.flags != 0) {
                return true;
            }
        } catch (RemoteException re) {
            throw new RuntimeException("Package manager has died", re);
        }

        return false;
    }

    void readOwnerFile() {
        try {
            InputStream input = openRead();
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(input, null);
            int type;
            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT) {
                if (type!=XmlPullParser.START_TAG) {
                    continue;
                }

                String tag = parser.getName();
                if (tag.equals(TAG_DEVICE_OWNER)) {
                    mDeviceOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
                    mDeviceOwnerName = parser.getAttributeValue(null, ATTR_NAME);
                } else if (tag.equals(TAG_PROFILE_OWNER)) {
                    String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
                    String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
                    int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID));
                    mProfileOwners.put(userId,
                            new String[] { profileOwnerPackageName, profileOwnerName });
                } else {
                    throw new XmlPullParserException(
                            "Unexpected tag in device owner file: " + tag);
                }
            }
            input.close();
        } catch (XmlPullParserException xppe) {
            Slog.e(TAG, "Error parsing device-owner file\n" + xppe);
        } catch (IOException ioe) {
            Slog.e(TAG, "IO Exception when reading device-owner file\n" + ioe);
        }
    }

    void writeOwnerFile() {
        synchronized (this) {
            writeOwnerFileLocked();
        }
    }

    private void writeOwnerFileLocked() {
        try {
            OutputStream outputStream = startWrite();
            XmlSerializer out = new FastXmlSerializer();
            out.setOutput(outputStream, "utf-8");
            out.startDocument(null, true);

            // Write device owner tag
            if (mDeviceOwnerPackageName != null) {
                out.startTag(null, TAG_DEVICE_OWNER);
                out.attribute(null, ATTR_PACKAGE, mDeviceOwnerPackageName);
                if (mDeviceOwnerName != null) {
                    out.attribute(null, ATTR_NAME, mDeviceOwnerName);
                }
                out.endTag(null, TAG_DEVICE_OWNER);
            }

            // Write profile owner tags
            if (mProfileOwners.size() > 0) {
                for (HashMap.Entry<Integer, String[]> owner : mProfileOwners.entrySet()) {
                    out.startTag(null, TAG_PROFILE_OWNER);
                    out.attribute(null, ATTR_PACKAGE, owner.getValue()[0]);
                    out.attribute(null, ATTR_NAME, owner.getValue()[1]);
                    out.attribute(null, ATTR_USERID, Integer.toString(owner.getKey()));
                    out.endTag(null, TAG_PROFILE_OWNER);
                }
            }
            out.endDocument();
            out.flush();
            finishWrite(outputStream);
        } catch (IOException ioe) {
            Slog.e(TAG, "IO Exception when writing device-owner file\n" + ioe);
        }
    }

    private InputStream openRead() throws IOException {
        if (mInputStreamForTest != null) {
            return mInputStreamForTest;
        }

        return new AtomicFile(new File(Environment.getSystemSecureDirectory(),
                DEVICE_OWNER_XML)).openRead();
    }

    private OutputStream startWrite() throws IOException {
        if (mOutputStreamForTest != null) {
            return mOutputStreamForTest;
        }

        fileForWriting = new AtomicFile(new File(Environment.getSystemSecureDirectory(),
                DEVICE_OWNER_XML));
        return fileForWriting.startWrite();
    }

    private void finishWrite(OutputStream stream) {
        if (fileForWriting != null) {
            fileForWriting.finishWrite((FileOutputStream) stream);
        }
    }
}
 No newline at end of file
+97 −118

File changed.

Preview size limit exceeded, changes collapsed.

Loading