blob: 9fd0e0960d894a5ab750f572000d7bdf0bbb9c41 [file] [log] [blame]
/*
* 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<Integer, OwnerInfo> mProfileOwners = new HashMap<Integer, OwnerInfo>();
// 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<Integer> 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<Integer, OwnerInfo> 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();
}
}
}