/* * 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.ComponentName; 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; import java.util.Set; /** * 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_COMPONENT_NAME = "component"; 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 OwnerInfo mDeviceOwner; // Internal state for the profile owner packages. private final HashMap mProfileOwners = new HashMap(); // 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.mDeviceOwner = new OwnerInfo(ownerName, packageName); return owner; } /** * @deprecated Use a component name instead of package name * 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 OwnerInfo(ownerName, packageName)); return owner; } /** * Creates an instance of the device owner object with the profile owner set. */ static DeviceOwner createWithProfileOwner(ComponentName admin, String ownerName, int userId) { DeviceOwner owner = new DeviceOwner(); owner.mProfileOwners.put(userId, new OwnerInfo(ownerName, admin)); return owner; } String getDeviceOwnerPackageName() { return mDeviceOwner != null ? mDeviceOwner.packageName : null; } String getDeviceOwnerName() { return mDeviceOwner != null ? mDeviceOwner.name : null; } void setDeviceOwner(String packageName, String ownerName) { mDeviceOwner = new OwnerInfo(ownerName, packageName); } void clearDeviceOwner() { mDeviceOwner = null; } /** * @deprecated */ void setProfileOwner(String packageName, String ownerName, int userId) { mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName)); } void setProfileOwner(ComponentName admin, String ownerName, int userId) { mProfileOwners.put(userId, new OwnerInfo(ownerName, admin)); } void removeProfileOwner(int userId) { mProfileOwners.remove(userId); } /** * @deprecated Use getProfileOwnerComponent * @param userId * @return */ String getProfileOwnerPackageName(int userId) { OwnerInfo profileOwner = mProfileOwners.get(userId); return profileOwner != null ? profileOwner.packageName : null; } ComponentName getProfileOwnerComponent(int userId) { OwnerInfo profileOwner = mProfileOwners.get(userId); return profileOwner != null ? profileOwner.admin : null; } String getProfileOwnerName(int userId) { OwnerInfo profileOwner = mProfileOwners.get(userId); return profileOwner != null ? profileOwner.name : null; } Set getProfileOwnerKeys() { return mProfileOwners.keySet(); } boolean hasDeviceOwner() { return mDeviceOwner != 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)) { String name = parser.getAttributeValue(null, ATTR_NAME); String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); mDeviceOwner = new OwnerInfo(name, packageName); } else if (tag.equals(TAG_PROFILE_OWNER)) { String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE); String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME); String profileOwnerComponentStr = parser.getAttributeValue(null, ATTR_COMPONENT_NAME); int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID)); OwnerInfo profileOwnerInfo = null; if (profileOwnerComponentStr != null) { ComponentName admin = ComponentName.unflattenFromString( profileOwnerComponentStr); if (admin != null) { profileOwnerInfo = new OwnerInfo(profileOwnerName, admin); } else { // This shouldn't happen but switch from package name -> component name // might have written bad device owner files. b/17652534 Slog.e(TAG, "Error parsing device-owner file. Bad component name " + profileOwnerComponentStr); } } if (profileOwnerInfo == null) { profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName); } mProfileOwners.put(userId, profileOwnerInfo); } 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 (mDeviceOwner != null) { out.startTag(null, TAG_DEVICE_OWNER); out.attribute(null, ATTR_PACKAGE, mDeviceOwner.packageName); if (mDeviceOwner.name != null) { out.attribute(null, ATTR_NAME, mDeviceOwner.name); } out.endTag(null, TAG_DEVICE_OWNER); } // Write profile owner tags if (mProfileOwners.size() > 0) { for (HashMap.Entry owner : mProfileOwners.entrySet()) { out.startTag(null, TAG_PROFILE_OWNER); OwnerInfo ownerInfo = owner.getValue(); out.attribute(null, ATTR_PACKAGE, ownerInfo.packageName); out.attribute(null, ATTR_NAME, ownerInfo.name); out.attribute(null, ATTR_USERID, Integer.toString(owner.getKey())); if (ownerInfo.admin != null) { out.attribute(null, ATTR_COMPONENT_NAME, ownerInfo.admin.flattenToString()); } 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); } } static class OwnerInfo { public String name; public String packageName; public ComponentName admin; public OwnerInfo(String name, String packageName) { this.name = name; this.packageName = packageName; this.admin = new ComponentName(packageName, ""); } public OwnerInfo(String name, ComponentName admin) { this.name = name; this.admin = admin; this.packageName = admin.getPackageName(); } } }